События клавиатуры в приложении WPF MVVM?

Вы не можете прервать поток, за исключением операции с коротким замыканием, которая оставила бы некоторые значения потока необработанными независимо от их значения. Но если вы просто хотите избежать операций над потоком, вы можете добавить преобразование и фильтр в поток:

import java.util.Objects;

class ThingProcessor
{
    static Thing returnNullOnCondition(Thing thing)
    {    return( (*** is condition met ***)? null : thing);    }

    void processThings(Collection<Thing> thingsCollection)
    {
        thingsCollection.stream()
        *** regular stream processing ***
        .map(ThingProcessor::returnNullOnCondition)
        .filter(Objects::nonNull)
        *** continue stream processing ***
    }
} // class ThingProcessor

Это преобразует поток вещей в нулевые значения, когда вещи соответствуют некоторому условию, а затем отфильтровывает обнуляет. Если вы готовы побаловать побочные эффекты, вы можете установить значение условия true, как только произойдет что-то, поэтому все последующие вещи отфильтровываются независимо от их значения. Но даже если вы не можете сэкономить много (если не совсем) обработку, фильтруя значения из потока, который вы не хотите обрабатывать.

48
задан Carlos 4 March 2009 в 23:25
поделиться

5 ответов

Немного поздно, но начнем.

Команда Microsoft WPF недавно выпустила раннюю версию своего WPF MVVM Toolkit . В нем вы найдете класс CommandReference, который может обрабатывать такие вещи, как привязки клавиш. Посмотрите на их шаблон WPF MVVM, чтобы узнать, как он работает.

8
ответ дан 7 November 2019 в 11:58
поделиться

Я изучил эту проблему несколько месяцев назад и написал расширение разметки, которое помогает решить эту проблему. Его можно использовать как обычную привязку:

<Window.InputBindings>
    <KeyBinding Key="E" Modifiers="Control" Command="{input:CommandBinding EditCommand}"/>
</Window.InputBindings>

Полный исходный код этого расширения можно найти здесь:

http://www.thomaslevesque.com/2009/03/17/wpf-using-inputbindings-with -the-mvvm-pattern /

Имейте в виду, что этот обходной путь, вероятно, не очень "чистый", потому что он использует некоторые частные классы и поля через отражение ...

3
ответ дан 7 November 2019 в 11:58
поделиться

Ничего себе, там около тысячи ответов, и здесь я собираюсь добавить еще один ..

Действительно очевидная вещь в "почему-я-не-понял-это" -форум-пощечина »- это то, что код программной части и ViewModel , так сказать, находятся в одной комнате, поэтому нет причин, по которым им не разрешено разговаривать.

Если задуматься, XAML уже тесно связан с API ViewModel, так что с таким же успехом можно пойти и сделать зависимость от него из стоящего за ним кода.

Остальные очевидные правила, которые следует соблюдать или игнорировать, по-прежнему применяются (интерфейсы, нулевые проверки <- особенно если вы используете Blend ...)

Я всегда делаю свойство в коде программной части следующим образом:

private ViewModelClass ViewModel { get { return DataContext as ViewModelClass; } }

Это это клиент-код. Нулевая проверка предназначена для помощи в управлении хостингом, как в blend.

void someEventHandler(object sender, KeyDownEventArgs e)
{
    if (ViewModel == null) return;
    /* ... */
    ViewModel.HandleKeyDown(e);
}

Обработайте свое событие в коде позади, как вы хотите (события пользовательского интерфейса ориентированы на пользовательский интерфейс, так что все в порядке), а затем иметь метод в ViewModelClass, который может реагировать на это событие. Проблемы все еще разделены.

ViewModelClass
{
    public void HandleKeyDown(KeyEventArgs e) { /* ... */ }
}

Все эти другие присоединенные свойства и вуду - это очень круто, и методы действительно полезны для некоторых других вещей, но здесь вы можете уйти с чем-нибудь попроще ...

30
ответ дан 7 November 2019 в 11:58
поделиться

Я делаю это при помощи приложенного поведения с 3 свойствами зависимости; каждый - команда для выполнения, каждый - параметр для передачи команде, и другой ключ, который заставит команду выполняться. Вот код:

public static class CreateKeyDownCommandBinding
{
    /// <summary>
    /// Command to execute.
    /// </summary>
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached("Command",
        typeof(CommandModelBase),
        typeof(CreateKeyDownCommandBinding),
        new PropertyMetadata(new PropertyChangedCallback(OnCommandInvalidated)));

    /// <summary>
    /// Parameter to be passed to the command.
    /// </summary>
    public static readonly DependencyProperty ParameterProperty =
        DependencyProperty.RegisterAttached("Parameter",
        typeof(object),
        typeof(CreateKeyDownCommandBinding),
        new PropertyMetadata(new PropertyChangedCallback(OnParameterInvalidated)));

    /// <summary>
    /// The key to be used as a trigger to execute the command.
    /// </summary>
    public static readonly DependencyProperty KeyProperty =
        DependencyProperty.RegisterAttached("Key",
        typeof(Key),
        typeof(CreateKeyDownCommandBinding));

    /// <summary>
    /// Get the command to execute.
    /// </summary>
    /// <param name="sender"></param>
    /// <returns></returns>
    public static CommandModelBase GetCommand(DependencyObject sender)
    {
        return (CommandModelBase)sender.GetValue(CommandProperty);
    }

    /// <summary>
    /// Set the command to execute.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="command"></param>
    public static void SetCommand(DependencyObject sender, CommandModelBase command)
    {
        sender.SetValue(CommandProperty, command);
    }

    /// <summary>
    /// Get the parameter to pass to the command.
    /// </summary>
    /// <param name="sender"></param>
    /// <returns></returns>
    public static object GetParameter(DependencyObject sender)
    {
        return sender.GetValue(ParameterProperty);
    }

    /// <summary>
    /// Set the parameter to pass to the command.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="parameter"></param>
    public static void SetParameter(DependencyObject sender, object parameter)
    {
        sender.SetValue(ParameterProperty, parameter);
    }

    /// <summary>
    /// Get the key to trigger the command.
    /// </summary>
    /// <param name="sender"></param>
    /// <returns></returns>
    public static Key GetKey(DependencyObject sender)
    {
        return (Key)sender.GetValue(KeyProperty);
    }

    /// <summary>
    /// Set the key which triggers the command.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="key"></param>
    public static void SetKey(DependencyObject sender, Key key)
    {
        sender.SetValue(KeyProperty, key);
    }

    /// <summary>
    /// When the command property is being set attach a listener for the
    /// key down event.  When the command is being unset (when the
    /// UIElement is unloaded for instance) remove the listener.
    /// </summary>
    /// <param name="dependencyObject"></param>
    /// <param name="e"></param>
    static void OnCommandInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        UIElement element = (UIElement)dependencyObject;
        if (e.OldValue == null && e.NewValue != null)
        {
            element.AddHandler(UIElement.KeyDownEvent,
                new KeyEventHandler(OnKeyDown), true);
        }

        if (e.OldValue != null && e.NewValue == null)
        {
            element.RemoveHandler(UIElement.KeyDownEvent,
                new KeyEventHandler(OnKeyDown));
        }
    }

    /// <summary>
    /// When the parameter property is set update the command binding to
    /// include it.
    /// </summary>
    /// <param name="dependencyObject"></param>
    /// <param name="e"></param>
    static void OnParameterInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        UIElement element = (UIElement)dependencyObject;
        element.CommandBindings.Clear();

        // Setup the binding
        CommandModelBase commandModel = e.NewValue as CommandModelBase;
        if (commandModel != null)
        {
            element.CommandBindings.Add(new CommandBinding(commandModel.Command,
            commandModel.OnExecute, commandModel.OnQueryEnabled));
        }
    }

    /// <summary>
    /// When the trigger key is pressed on the element, check whether
    /// the command should execute and then execute it.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    static void OnKeyDown(object sender, KeyEventArgs e)
    {
        UIElement element = sender as UIElement;
        Key triggerKey = (Key)element.GetValue(KeyProperty);

        if (e.Key != triggerKey)
        {
            return;
        }

        CommandModelBase cmdModel = (CommandModelBase)element.GetValue(CommandProperty);
        object parameter = element.GetValue(ParameterProperty);
        if (cmdModel.CanExecute(parameter))
        {
            cmdModel.Execute(parameter);
        }
        e.Handled = true;
    }
}

Для использования этого от xaml можно сделать что-то вроде этого:

<TextBox framework:CreateKeyDownCommandBinding.Command="{Binding MyCommand}">
    <framework:CreateKeyDownCommandBinding.Key>Enter</framework:CreateKeyDownCommandBinding.Key>
</TextBox>

Редактирование: CommandModelBase является базовым классом, который я использую для всех команд. Это основано на классе CommandModel от статьи Dan Crevier о MVVM ( здесь ). Вот источник для немного измененной версии, которую я использую с CreateKeyDownCommandBinding:

public abstract class CommandModelBase : ICommand
    {
        RoutedCommand routedCommand_;

        /// <summary>
        /// Expose a command that can be bound to from XAML.
        /// </summary>
        public RoutedCommand Command
        {
            get { return routedCommand_; }
        }

        /// <summary>
        /// Initialise the command.
        /// </summary>
        public CommandModelBase()
        {
            routedCommand_ = new RoutedCommand();
        }

        /// <summary>
        /// Default implementation always allows the command to execute.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnQueryEnabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanExecute(e.Parameter);
            e.Handled = true;
        }

        /// <summary>
        /// Subclasses must provide the execution logic.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnExecute(object sender, ExecutedRoutedEventArgs e)
        {
            Execute(e.Parameter);
        }

        #region ICommand Members

        public virtual bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public abstract void Execute(object parameter);

        #endregion
    }

Комментарии и предложения для улучшений очень приветствовались бы.

8
ответ дан Paul 7 November 2019 в 21:58
поделиться

Короткий ответ - Вы, не может обработать прямые события ввода с клавиатуры без кода - позади, но можно обработать InputBindings с MVVM (я могу показать Вам соответствующий пример, если это - то, в чем Вы нуждаетесь).

можно ли предоставить больше информации о том, что Вы хотите сделать в обработчике?

Кода - позади нельзя избежать полностью с MVVM. Это должно просто использоваться для строго связанных с UI задач. Кардинальный пример имел бы некоторый тип 'формы ввода данных', которая при загрузке должна установить фокус на первый входной элемент (текстовое поле, поле комбинированного списка, безотносительно). Вы обычно присваивали бы тому элементу атрибут x:Name, затем поднимали бы трубку событие 'Loaded' Window/Page/UserControl для установки фокуса на тот элемент. Это совершенно в порядке шаблоном, потому что задача центральна UI и не имеет никакого отношения к данным, которые это представляет.

2
ответ дан Adrian 7 November 2019 в 21:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: