WPF MVVM应用程序中的键盘事件?

WPF MVVM应用程序中的键盘事件?,wpf,mvvm,Wpf,Mvvm,如何在不使用代码隐藏的情况下处理Keyboard.KeyDown事件?我们试图使用MVVM模式,避免在代码隐藏文件中编写事件处理程序。简单的回答是,如果没有代码隐藏,您无法处理直接的键盘输入事件,但您可以使用MVVM处理输入绑定(如果您需要,我可以向您展示一个相关示例) 你能提供更多关于你想在处理程序中做什么的信息吗 MVVM不能完全避免代码隐藏。它只是用于严格的UI相关任务。一个主要的例子是具有某种类型的“数据输入表单”,加载时需要将焦点设置为第一个输入元素(文本框、组合框等)。您通常会为该元

如何在不使用代码隐藏的情况下处理Keyboard.KeyDown事件?我们试图使用MVVM模式,避免在代码隐藏文件中编写事件处理程序。

简单的回答是,如果没有代码隐藏,您无法处理直接的键盘输入事件,但您可以使用MVVM处理输入绑定(如果您需要,我可以向您展示一个相关示例)

你能提供更多关于你想在处理程序中做什么的信息吗


MVVM不能完全避免代码隐藏。它只是用于严格的UI相关任务。一个主要的例子是具有某种类型的“数据输入表单”,加载时需要将焦点设置为第一个输入元素(文本框、组合框等)。您通常会为该元素分配一个x:Name属性,然后连接Window/Page/UserControl的“Loaded”事件以设置该元素的焦点。根据模式,这是完全可以的,因为任务是以用户界面为中心的,与它所表示的数据无关。

我通过使用带有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;
    }
}
公共静态类CreateKeyDownCommandBinding
{
/// 
///要执行的命令。
/// 
公共静态只读DependencyProperty CommandProperty=
DependencyProperty.RegisterAttached(“命令”,
类型(CommandModelBase),
类型(CreateKeyDownCommandBinding),
新财产元数据(新财产变更回拨(未经命令和验证));
/// 
///要传递给命令的参数。
/// 
公共静态只读从属属性ParameterProperty=
DependencyProperty.RegisterAttached(“参数”,
类型(对象),
类型(CreateKeyDownCommandBinding),
新PropertyMetadata(新PropertyChangedCallback(OnParameterInvalidated));
/// 
///用作执行命令的触发器的键。
/// 
公共静态只读DependencyProperty KeyProperty=
DependencyProperty.RegisterAttached(“密钥”,
类型(键),
typeof(CreateKeyDownCommandBinding));
/// 
///获取要执行的命令。
/// 
/// 
/// 
公共静态命令ModelBase GetCommand(DependencyObject发送方)
{
return(CommandModelBase)sender.GetValue(CommandProperty);
}
/// 
///设置要执行的命令。
/// 
/// 
/// 
公共静态void SetCommand(DependencyObject发送器,CommandModelBase命令)
{
sender.SetValue(CommandProperty,command);
}
/// 
///获取要传递给命令的参数。
/// 
/// 
/// 
公共静态对象GetParameter(DependencyObject发送方)
{
返回sender.GetValue(ParameterProperty);
}
/// 
///设置要传递给命令的参数。
/// 
/// 
/// 
公共静态void SetParameter(DependencyObject发送器,对象参数)
{
sender.SetValue(参数属性,参数);
}
/// 
///获取触发命令的键。
/// 
/// 
/// 
公共静态密钥GetKey(DependencyObject发送方)
{
return(Key)sender.GetValue(KeyProperty);
}
/// 
///设置触发命令的键。
/// 
/// 
/// 
公共静态无效设置密钥(DependencyObject发送方,密钥)
{
sender.SetValue(KeyProperty,key);
}
/// 
///设置命令属性时,为
///当命令被取消设置时(当
///UIElement已卸载(例如)删除侦听器。
/// 
/// 
/// 
命令上的静态无效已验证(DependencyObject DependencyObject,DependencyPropertyChangedEventArgs e)
{
UIElement=(UIElement)dependencyObject;
如果(e.OldValue==null&&e.NewValue!=null)
{
element.AddHandler(UIElement.KeyDownEvent,
新的KeyEventHandler(OnKeyDown),true);
}
如果(e.OldValue!=null&&e.NewValue==null)
{
element.RemoveHandler(UIElement.KeyDownEvent,
新的KeyEventHandler(OnKeyDown));
}
}
/// 
///设置参数属性后,将命令绑定更新为
///包括它。
/// 
/// 
/// 
静态void OnParameterInvalidated(DependencyObject DependencyObject,DependencyPropertyChangedEventArgs e)
{
UIElement=(UIElement)dependencyObject;
element.CommandBindings.Clear();
//设置绑定
CommandModelBase commandModel=e.NewValue作为CommandModelBase;
if(commandModel!=null)
{
元素.CommandBindings.Add(新建CommandBinding(commandModel.Command),
commandModel.OneExecute、commandModel.OnQueryEnabled);
}
}
/// 
///当按下元件上的触发键时,检查是否
///命令应该先执行,然后再执行。
/// 
/// 
/// 
静态void OnKeyDown(对象发送方,KeyEventArgs e)
{
UIElement=发送方作为UIElement;
Key triggerKey=(Key)元素.GetValue(KeyProperty);
如果(e.Key!=触发器Key)
{
返回;
}
CommandModelBase cmdModel=(CommandModelBase)元素.GetValue(CommandProperty);
对象参数=element.GetValue(ParameterProperty);
if(cmdModel.CanExecute(参数))
{
cmdModel.Execute(参数);
}
e、 已处理=正确;
}
}
要从xaml中使用它,可以执行以下操作:

<TextBox framework:CreateKeyDownCommandBinding.Command="{Binding MyCommand}">
    <framework:CreateKeyDownCommandBinding.Key>Enter</framework:CreateKeyDownCommandBinding.Key>
</TextBox>
private ViewModelClass ViewModel { get { return DataContext as ViewModelClass; } }
<TextBox AcceptsReturn="False">
    <TextBox.InputBindings>
        <KeyBinding 
            Key="Enter" 
            Command="{Binding SearchCommand}" 
            CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" />
    </TextBox.InputBindings>
</TextBox>

进入
Edit:CommandModelBase是我用于所有命令的基类。它基于Dan Crevier关于MVVM()的文章中的CommandModel类。这是稍微修改过的版本I的源代码
private ViewModelClass ViewModel { get { return DataContext as ViewModelClass; } }
void someEventHandler(object sender, KeyDownEventArgs e)
{
    if (ViewModel == null) return;
    /* ... */
    ViewModel.HandleKeyDown(e);
}
ViewModelClass
{
    public void HandleKeyDown(KeyEventArgs e) { /* ... */ }
}
<TextBox AcceptsReturn="False">
    <TextBox.InputBindings>
        <KeyBinding 
            Key="Enter" 
            Command="{Binding SearchCommand}" 
            CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" />
    </TextBox.InputBindings>
</TextBox>
public class KeyToCommandExtension : IMarkupExtension<Delegate>
{
    public string Command { get; set; }
    public Key Key { get; set; }

    private void KeyEvent(object sender, KeyEventArgs e)
    {
        if (Key != Key.None && e.Key != Key) return;

        var target = (FrameworkElement)sender;

        if (target.DataContext == null) return;

        var property = target.DataContext.GetType().GetProperty(Command, BindingFlags.Public | BindingFlags.Instance, null, typeof(ICommand), new Type[0], null);

        if (property == null) return;

        var command = (ICommand)property.GetValue(target.DataContext, null);

        if (command != null && command.CanExecute(Key))
            command.Execute(Key);
    }

    public Delegate ProvideValue(IServiceProvider serviceProvider)
    {
        if (string.IsNullOrEmpty(Command))
            throw new InvalidOperationException("Command not set");

        var targetProvider = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));

        if (!(targetProvider.TargetObject is FrameworkElement))
            throw new InvalidOperationException("Target object must be FrameworkElement");

        if (!(targetProvider.TargetProperty is EventInfo))
            throw new InvalidOperationException("Target property must be event");

        return Delegate.CreateDelegate(typeof(KeyEventHandler), this, "KeyEvent");
    }
<TextBox KeyUp="{MarkupExtensions:KeyToCommand Command=LoginCommand, Key=Enter}"/>
<TextBox Text="{Binding UploadNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <TextBox.InputBindings>
        <KeyBinding Key="Enter" Command="{Binding FindUploadCommand}" />
    </TextBox.InputBindings>
</TextBox>