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