Keyboard events in MVF MVPF application?

How can I handle the Keyboard.KeyDown event without using code? We are trying to use the MVVM pattern and avoid writing an event handler in the code file.

+42
wpf mvvm
Mar 04 '09 at 23:25
source share
8 answers

A little late, but here it goes.

Microsoft's WPF team recently released an early version of its

+8
May 27 '09 at 21:20
source share

To bring an updated answer, framework. framework 4.0 allows you to do this beautifully, allowing you to bind the KeyBinding command to the command in view mode.

So ... If you want to listen to the Enter key, you would do something like this:

<TextBox AcceptsReturn="False"> <TextBox.InputBindings> <KeyBinding Key="Enter" Command="{Binding SearchCommand}" CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" /> </TextBox.InputBindings> </TextBox> 
+186
Aug 17 2018-11-11T00:
source share

WOW - there are like a thousand answers, and here I am going to add another one.

The really obvious thing in the why-not-I-implement-this-forehead-slap method is that the code-behind and the ViewModel are sitting in the same room to talk, so there is no reason why they are not allowed talk.

If you think about it, XAML is already closely connected with the ViewModel API, so you can just go and depend on it behind the code.

Other obvious rules for submission or ignoring still apply (interfaces, null checks <- especially if you use Blend ...)

I always make a property in code like this:

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

This is the client code. Zero check is intended for hosting management, as in blend.

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

Manage your event in the code you want (the user interface events are user-oriented, so that's fine), and then use the ViewModelClass method, which can respond to this event. The problems are still divided.

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

All these other attached properties and voodoo are very cool, and the methods are really useful for some other things, but here you can get away with something simpler ...

+25
Sep 18 '09 at 11:36
source share

I do this using the attached behavior with 3 dependency properties; one is the command to execute, one is the parameter to be passed to the command, and the other is the key that will trigger the command. Here is the code:

 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; } } 

To use this from xaml, you can do something like this:

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

Edit: CommandModelBase is the base class that I use for all commands. It is based on the CommandModel class from Dan Crevier's article on MVVM ( here ). Here is the source for the slightly modified version that I use with 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 } 

Comments and suggestions for improvement would be greatly appreciated.

+8
Mar 10 '09 at 9:36
source share

The short answer is: you cannot handle direct keyboard input events without code, but you can handle InputBindings with MVVM (I can show you the corresponding example if that is what you need).

Can you provide more information about what you want to do in the handler?

Code code should not be completely eliminated using MVVM. It is just used for UI related tasks. A cardinal example will have some type of โ€œdata entry formโ€, which when loading should set focus on the first input element (text field, combo box, whatever). Usually, you assign this element to the x: Name attribute, and then hook up the "Loaded" Window / Page / UserControl "Loaded" event to set focus on that element. This is quite normal in pattern because the task is UI oriented and has nothing to do with the data it represents.

+2
Mar 05 '09 at 0:13
source share

I studied this question a few months ago, and I wrote a markup extension that does the trick. It can be used as a regular binding:

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

The full source code for this extension can be found here:

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

Remember that this workaround is probably not very โ€œcleanโ€ because it uses some private classes and fields through reflection ...

+2
May 6 '09 at 23:11
source share

I know this question is very old, but I came to this because this type of functionality was just easier implemented in Silverlight (5). So maybe others will come here too.

I wrote this simple solution after I could not find what I was looking for. It turned out to be quite simple. It should work in both Silverlight 5 and WPF.

 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"); } 

Using:

 <TextBox KeyUp="{MarkupExtensions:KeyToCommand Command=LoginCommand, Key=Enter}"/> 

Note that Command is a string, not an ICommand binding. I know that this is not so flexible, but when used it is cleaner and that you need 99% of the time. Although this should not be a problem for change.

+1
Sep 01 '11 at 13:26
source share

Like karlipoppins, but I found that it does not work without the following changes / additions:

 <TextBox Text="{Binding UploadNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> <TextBox.InputBindings> <KeyBinding Key="Enter" Command="{Binding FindUploadCommand}" /> </TextBox.InputBindings> </TextBox> 
0
Jun 09 '16 at 7:00
source share