如何在不使用代码隐藏的情况下处理Keyboard.KeyDown事件?我们正在尝试使用MVVM模式,并避免在代码隐藏文件中编写事件处理程序.
kar*_*ins 218
为了获得更新的答案,.net 4.0框架使您可以通过将KeyBinding命令绑定到viewmodel中的命令来很好地完成此操作.
所以......如果你想听Enter键,你会做这样的事情:
<TextBox AcceptsReturn="False">
<TextBox.InputBindings>
<KeyBinding
Key="Enter"
Command="{Binding SearchCommand}"
CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
Run Code Online (Sandbox Code Playgroud)
Pie*_*eed 30
哇 - 有一千个答案,在这里我要再加一个......
在'为什么 - 我没有意识到 - 这个 - 前额 - 一巴掌'的方式中,真正明显的事情是代码隐藏和ViewModel坐在同一个房间里可以说话,所以没有理由他们为什么不被允许进行对话.
如果你考虑一下,XAML已经与ViewModel的API紧密结合,所以你也可以从后面的代码中依赖它.
服从或忽略的其他明显规则仍然适用(接口,空检查< - 特别是如果你使用Blend ......)
我总是在代码隐藏中创建一个属性,如下所示:
private ViewModelClass ViewModel { get { return DataContext as ViewModelClass; } }
Run Code Online (Sandbox Code Playgroud)
这是客户端代码.空检查用于帮助控制托管,就像在混合中一样.
void someEventHandler(object sender, KeyDownEventArgs e)
{
if (ViewModel == null) return;
/* ... */
ViewModel.HandleKeyDown(e);
}
Run Code Online (Sandbox Code Playgroud)
像你想要的那样在你背后的代码中处理你的事件(UI事件是以UI为中心的,所以没关系)然后在ViewModelClass上有一个可以响应该事件的方法.这些担忧仍然存在分歧.
ViewModelClass
{
public void HandleKeyDown(KeyEventArgs e) { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)
所有这些其他附加的属性和伏都教非常酷,这些技术对其他一些东西非常有用,但在这里你可以通过更简单的东西逃脱...
我通过使用具有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;
}
}
Run Code Online (Sandbox Code Playgroud)
要从xaml中使用它,您可以执行以下操作:
<TextBox framework:CreateKeyDownCommandBinding.Command="{Binding MyCommand}">
<framework:CreateKeyDownCommandBinding.Key>Enter</framework:CreateKeyDownCommandBinding.Key>
</TextBox>
Run Code Online (Sandbox Code Playgroud)
编辑: CommandModelBase是我用于所有命令的基类.它基于Dan Crevier关于MVVM的文章中的CommandModel类(此处).这是我与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
}
Run Code Online (Sandbox Code Playgroud)
非常欢迎提出改进意见和建议.
有点晚了,但到了.
微软的WPF团队最近发布了他们的WPF MVVM Toolkit的早期版本 .在其中,您将找到一个名为CommandReference的类,它可以处理键绑定之类的事情.查看他们的WPF MVVM模板,看看它是如何工作的.
| 归档时间: |
|
| 查看次数: |
73581 次 |
| 最近记录: |