Handling Key press & release events in MVVM - wpf

I am developing a wpf application using MVVM pattern. I need to separately handle Key press & release events, (e.g. in media players fwd/rev happen till user keeps key pressed & stops when he releases).
After searching a lot, still I couldn't find any way to do it. Can anybody please help.

Thanks for your suggestions.
I found a way to do this by using interactivity triggers & dependency property.
Following is the dependency property for Command.
public class EventToCommand : TriggerAction<DependencyObject>
{
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
// Using a DependencyProperty as the backing store for Command. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommand), new PropertyMetadata(null));
protected override void Invoke(object parameter)
{
if (Command != null
&& Command.CanExecute(parameter))
{
Command.Execute(parameter);
}
}
}
Then just use it in the xaml as below:
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<ap:EventToCommand Command="{Binding KeyReleaseCommand}"></ap:EventToCommand>
</i:EventTrigger>
<i:EventTrigger EventName="KeyDown">
<ap:EventToCommand Command="{Binding KeyDownCommand}"></ap:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
Where the KeyReleaseCommand & KeyDownCommand are RelayCommand in your ViewModel.
public MainViewModel()
{
KeyDownCommand = new RelayCommand<KeyEventArgs>(OnKeyDown, null);
KeyReleaseCommand = new RelayCommand<KeyEventArgs>(OnKeyRelease, null);
}
private void OnKeyRelease(KeyEventArgs args)
{
if (args.KeyboardDevice.Modifiers == ModifierKeys.Alt)
{
if (args.SystemKey == Key.Left)
{
Trace.WriteLine("ALT+LEFT Released");
}
}
}
public void OnKeyDown(KeyEventArgs args)
{
if (args.IsRepeat)
return;
if (args.KeyboardDevice.Modifiers == ModifierKeys.Alt)
{
if(args.SystemKey == Key.Left)
{
Trace.WriteLine("ALT+LEFT");
}
}
}

I'm guessing, You will bind the Command to Button. If you want the Commands to fire repeatedly, you can use RepeatButton. It was designed for that purpose.You can bind your command to Command Property. It will fire your methods repeatedly until RepeatButton is released.

Related

WPF MVVM cancel Window.Closing event

In WPF application together with MVVMLight Toolkit, I would like to see your opinion, what is the best way to implement if I need to Cancel the Window Close event.
In Window.Closing event I can set the e.Cancel = true, which prevents closing the form. To identify if the Close is allowed, or should be prevented is in the ViewModel context.
One solution could be if I define an Application variable, and I can query this in the normal event handler in view code behind?
thanks
With MVVM Light you got EventToCommand:
So you could in xaml wire up the closing event to the VM.
<Window ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<command:EventToCommand Command="{Binding ClosingCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
and in the VM:
public RelayCommand<CancelEventArgs> ClosingCommand { get; private set; }
ctor() {
ClosingCommand = new RelayCommand<CancelEventArgs>(args => args.Cancel = true);
}
If you do not want to pass CancelEventArgs to the VM:
You could always take the similar approach with a Behavior and just use a simple bool from the VM(bind this bool to the Behavior) to indicate the closing event should be cancelled.
Update:
Download Link for following example
To do this with a Behavior you could just have a Behavior such as:
internal class CancelCloseWindowBehavior : Behavior<Window> {
public static readonly DependencyProperty CancelCloseProperty =
DependencyProperty.Register("CancelClose", typeof(bool),
typeof(CancelCloseWindowBehavior), new FrameworkPropertyMetadata(false));
public bool CancelClose {
get { return (bool) GetValue(CancelCloseProperty); }
set { SetValue(CancelCloseProperty, value); }
}
protected override void OnAttached() {
AssociatedObject.Closing += (sender, args) => args.Cancel = CancelClose;
}
}
Now in xaml:
<i:Interaction.Behaviors>
<local:CancelCloseWindowBehavior CancelClose="{Binding CancelClose}" />
</i:Interaction.Behaviors>
Where CancelClose is a bool property from the VM which indicates if the Closing event should be cancelled or not. In the attached example I have a Button to toggle this bool from the VM that should let you test the Behavior
You could to control this using Messages, for instance:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<CloseApplicationMessage>(this, m => Close());
Loaded += MainWindowLoaded;
Closing += MainWindowClosing;
}
private void MainWindowClosing(object sender, CancelEventArgs e)
{
//Ask for saving
var closingMessage = new ClosingApplicationMessage();
Messenger.Default.Send(closingMessage);
if (closingMessage.Cancel)
e.Cancel = true;
}
...
The mvvm message:
public class ClosingApplicationMessage
{
public bool Cancel { get; set; }
}
In this way, in any place you are listening to the ClosingApplicationMessage, you can control when the application is going to close, and may to cancel it.
Hope this helps...

How to set focus on AND SELECT ALL of an initial text box (MVVM-Style)?

I have a simple WPF page with one text box field that my client wants highlighted when the page shows up. In code behind, it would be three lines, but I'm sogging through MVVM (which I'm starting to think is a little over-rated). I've tried so many different variants of behaviors and global events and FocusManager.FocusedElement, but nothing I do will do this.
Ultimately the most of the code I've been using calls these two lines:
Keyboard.Focus(textBox);
textBox.SelectAll();
But no matter where I put these lines the text box is only focused; no text is selected. I have never had this much trouble with something so simple. I've been hitting my head against the internets for two hours. Does anyone know how to do this?
Again, all I want to do is have the text box focus and it's text all selected when the page is navigated to. Please help!
"Focus" and "Select All Text from a TextBox" is a View-specific concern.
Put that in code Behind. It does not break the MVVM separation at all.
public void WhateverControl_Loaded(stuff)
{
Keyboard.Focus(textBox);
textBox.SelectAll();
}
If you need to do it in response to a specific application/business logic. Create an Attached Property.
Or:
have your View resolve the ViewModel by:
this.DataContext as MyViewModel;
then create some event in the ViewModel to which you can hook:
public class MyViewModel
{
public Action INeedToFocusStuff {get;set;}
public void SomeLogic()
{
if (SomeCondition)
INeedToFocusStuff();
}
}
then hook it up in the View:
public void Window_Loaded(Or whatever)
{
var vm = this.DataContext as MyViewModel;
vm.INeedToFocusStuff += FocusMyStuff;
}
public void FocusMyStuff()
{
WhateverTextBox.Focus();
}
See how this simple abstraction keeps View related stuff in the View and ViewModel related stuff in the ViewModel, while allowing them to interact. Keep it Simple. You don't need NASA's servers for a WPF app.
And no MVVM is not overrated, MVVM is extremely helpful and I would say even necessary. You'll quickly realize this as soon as you begin working with ItemsControls such as ListBoxes or DataGrids.
Here are some workthroughs:
Use Interaction.Behaviors
You can install the NuGet package named Microsoft.Xaml.Behaviors.Wpf, and write your own Behavior:
using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;
public class AutoSelectAllBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
}
private void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
{
if (AssociatedObject is TextBox box)
box.SelectAll();
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
}
}
and attach this behavior to the TextBox in the xaml:
<!-- xmlns:i="http://schemas.microsoft.com/xaml/behaviors" -->
<TextBox>
<i:Interaction.Behaviors>
<br:AutoSelectAllBehavior />
</i:Interaction.Behaviors>
</TextBox>
Use Interaction.Triggers
This is in the same package as mentioned in the last section. This special can be considered to let you be able to bind UIElement events to your ViewModel.
In your ViewModel, suppose you have an ICommand relay command (You may also need Microsoft.Toolkit.MVVM so that you can use some handy relay commands):
public ICommand SelectAllCommand { get; }
public ViewModel()
{
SelectAllCommand = new RelayCommand<TextBox>(box => box.SelectAll());
}
and then attach this command to the TextBox by setting the triggers:
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding SelectAllCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Use Attached Property
You can also use attached property (write your own class derived from TextBox and use dependency property is quite similar):
using System.Windows;
using System.Windows.Controls;
public class TextBoxProperties
{
public static bool GetAutoSelectAll(DependencyObject obj)
{
return (bool)obj.GetValue(AutoSelectAllProperty);
}
public static void SetAutoSelectAll(DependencyObject obj, bool value)
{
obj.SetValue(AutoSelectAllProperty, value);
}
public static readonly DependencyProperty AutoSelectAllProperty =
DependencyProperty.RegisterAttached("AutoSelectAll", typeof(bool), typeof(TextBoxProperties), new PropertyMetadata(false, TextBoxProperties_PropertyChanged));
private static void TextBoxProperties_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
if (d is TextBox box)
{
box.GotFocus += TextBox_GotFocus;
}
}
}
private static void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
var box = sender as TextBox;
box.SelectAll();
}
}
Then you can use it like:
<!-- xmlns:ap="..." -->
<TextBox ap:TextBoxProperties.AutoSelectAll="True" />

Set focus on another control when a key pressed without code-behind

I'm implementing something like an autosuggestion control: I have a user control that contains a TextBox and a ListBox. When the user enters text I'm handing it with System.Windows.Interactivity behaviors and filling the ListBox with some values...
Everything works fine... but I want to enable the user to select items in the ListBox (i.e. to set Focus on the ListBox) when the down arrow key is pressed.
I know that it is possible to handle the KeyPressDown event of the TextBox in the code-behind .cs file but how can I avoid this?
If you already use Interactivity that should not be much of an issue, just implement your own TriggerAction that has the properties Key & TargetName to indentify when and what to focus. Set it in an EventTrigger for PreviewKeyDown.
Sample implementation & use:
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyDown">
<t:KeyDownFocusAction Key="Down"
Target="{Binding ElementName=lbx}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<ListBox Name="lbx" ItemsSource="{Binding Data}" />
class KeyDownFocusAction : TriggerAction<UIElement>
{
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register("Key", typeof(Key), typeof(KeyDownFocusAction));
public Key Key
{
get { return (Key)GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.Register("Target", typeof(UIElement), typeof(KeyDownFocusAction), new UIPropertyMetadata(null));
public UIElement Target
{
get { return (UIElement)GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}
protected override void Invoke(object parameter)
{
if (Keyboard.IsKeyDown(Key))
{
Target.Focus();
}
}
}
Tested it and it works, note that KeyDown does not because the arrow keys are intercepted and marked as handled by the TextBox.
I don't think you can avoid it
What's wrong with capturing the KeyDown event of the TextBox and if it's an Up or Down arrow key, just trigger the ListBox.KeyDown event in the code behind?
I see no reason not to use code-behind in MVVM if it is to provide view-specific functionality such as focus
This answer is based on the one from H.B., and adds support for checking to see if the Ctrl key is pressed. This means it can handle keycombinations such as Ctrl-F for find.
XAML
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyDown">
<t:KeyDownFocusAction Key="Down" Ctrl="True"
Target="{Binding ElementName=lbx}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<ListBox Name="lbx" ItemsSource="{Binding Data}" />
Namespaces
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
See help on adding System.Windows.Interactivity.
DependencyProperty
public class KeyDownFocusAction : TriggerAction<UIElement>
{
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register("Key", typeof(Key), typeof(KeyDownFocusAction));
public Key Key
{
get { return (Key)GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
public static readonly DependencyProperty CtrlProperty =
DependencyProperty.Register("Ctrl", typeof(bool), typeof(KeyDownFocusAction));
public bool Ctrl
{
get { return (bool)GetValue(CtrlProperty); }
set { SetValue(CtrlProperty, value); }
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.Register("Target", typeof(UIElement), typeof(KeyDownFocusAction), new UIPropertyMetadata(null));
public UIElement Target
{
get { return (UIElement)GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}
protected override void Invoke(object parameter)
{
if (Keyboard.IsKeyDown(Key))
{
if (Ctrl == true)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
Target.Focus();
}
}
}
}
}

Invoke Command When "ENTER" Key Is Pressed In XAML

I want to invoke a command when ENTER is pressed in a TextBox. Consider the following XAML:
<UserControl
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
...>
...
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction Command="{Binding MyCommand}"
CommandParameter="{Binding Text}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
...
</UserControl>
and that MyCommand is as follows:
public ICommand MyCommand {
get { return new DelegateCommand<string>(MyCommandExecute); }
}
private void MyCommandExecute(string s) { ... }
With the above, my command is invoked for every key press. How can I restrict the command to only invoke when the ENTER key is pressed?
I understand that with Expression Blend I can use Conditions but those seem to be restricted to elements and can't consider event arguments.
I have also come across SLEX which offers its own InvokeCommandAction implementation that is built on top of the Systems.Windows.Interactivity implementation and can do what I need. Another consideration is to write my own trigger, but I'm hoping there's a way to do it without using external toolkits.
There is KeyTrigger in expression blend.
<UserControl
xmlns:i="clr-namespace:System.Windows.Interactivity;
assembly=System.Windows.Interactivity"
xmlns:iex="clr-namespace:Microsoft.Expression.Interactivity.Input;
assembly=Microsoft.Expression.Interactions" ...>
<TextBox>
<i:Interaction.Triggers>
<iex:KeyTrigger Key="Enter">
<i:InvokeCommandAction Command="{Binding PasswordLoginCommand}" />
</iex:KeyTrigger>
</i:Interaction.Triggers>
</TextBox>
</UserControl>
System.Windows.Interactivity and Microsoft.Expression.Interactions assemblies are available for WPF in the official Nuget package.
I like scottrudy's approach (to which I've given a +1) with the custom triggers approach as it stays true to my initial approach. I'm including a modified version of it below to use dependency properties instead of reflection info so that it's possible to bind directly to the ICommand. I'm also including an approach using attached properties to avoid using System.Windows.Interactivity if desired. The caveat to the latter approach is that you lose the feature of multiple invokations from an event, but you can apply it more generally.
Custom Triggers Approach
ExecuteCommandAction.cs
public class ExecuteCommandAction : TriggerAction<DependencyObject> {
#region Properties
public ICommand Command {
get { return (ICommand)base.GetValue(CommandProperty); }
set { base.SetValue(CommandProperty, value); }
}
public static ICommand GetCommand(DependencyObject obj) {
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value) {
obj.SetValue(CommandProperty, value);
}
// We use a DependencyProperty so we can bind commands directly rather
// than have to use reflection info to find them
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(ExecuteCommandAction), null);
#endregion Properties
protected override void Invoke(object parameter) {
ICommand command = Command ?? GetCommand(AssociatedObject);
if (command != null && command.CanExecute(parameter)) {
command.Execute(parameter);
}
}
}
TextBoxEnterKeyTrigger.cs
public class TextBoxEnterKeyTrigger : TriggerBase<UIElement> {
protected override void OnAttached() {
base.OnAttached();
TextBox textBox = this.AssociatedObject as TextBox;
if (textBox != null) {
this.AssociatedObject.KeyUp += new System.Windows.Input.KeyEventHandler(AssociatedObject_KeyUp);
}
else {
throw new InvalidOperationException("This behavior only works with TextBoxes");
}
}
protected override void OnDetaching() {
base.OnDetaching();
AssociatedObject.KeyUp -= new KeyEventHandler(AssociatedObject_KeyUp);
}
private void AssociatedObject_KeyUp(object sender, KeyEventArgs e) {
if (e.Key == Key.Enter) {
TextBox textBox = AssociatedObject as TextBox;
//This checks for an mvvm style binding and updates the source before invoking the actions.
BindingExpression expression = textBox.GetBindingExpression(TextBox.TextProperty);
if (expression != null)
expression.UpdateSource();
InvokeActions(textBox.Text);
}
}
}
MyUserControl.xaml
<UserControl
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:b="clr-namespace:MyNameSpace.Interactivity"
...
<TextBox>
<i:Interaction.Triggers>
<b:TextBoxEnterKeyTrigger>
<b:ExecuteCommandAction Command="{Binding MyCommand}" />
</b:TextBoxEnterKeyTrigger>
</i:Interaction.Triggers>
</TextBox>
...
</UserControl>
Attached Properties Approach
EnterKeyDown.cs
public sealed class EnterKeyDown {
#region Properties
#region Command
public static ICommand GetCommand(DependencyObject obj) {
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value) {
obj.SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(EnterKeyDown),
new PropertyMetadata(null, OnCommandChanged));
#endregion Command
#region CommandArgument
public static object GetCommandArgument(DependencyObject obj) {
return (object)obj.GetValue(CommandArgumentProperty);
}
public static void SetCommandArgument(DependencyObject obj, object value) {
obj.SetValue(CommandArgumentProperty, value);
}
public static readonly DependencyProperty CommandArgumentProperty =
DependencyProperty.RegisterAttached("CommandArgument", typeof(object), typeof(EnterKeyDown),
new PropertyMetadata(null, OnCommandArgumentChanged));
#endregion CommandArgument
#region HasCommandArgument
private static bool GetHasCommandArgument(DependencyObject obj) {
return (bool)obj.GetValue(HasCommandArgumentProperty);
}
private static void SetHasCommandArgument(DependencyObject obj, bool value) {
obj.SetValue(HasCommandArgumentProperty, value);
}
private static readonly DependencyProperty HasCommandArgumentProperty =
DependencyProperty.RegisterAttached("HasCommandArgument", typeof(bool), typeof(EnterKeyDown),
new PropertyMetadata(false));
#endregion HasCommandArgument
#endregion Propreties
#region Event Handling
private static void OnCommandArgumentChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
SetHasCommandArgument(o, true);
}
private static void OnCommandChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
FrameworkElement element = o as FrameworkElement;
if (element != null) {
if (e.NewValue == null) {
element.KeyDown -= new KeyEventHandler(FrameworkElement_KeyDown);
}
else if (e.OldValue == null) {
element.KeyDown += new KeyEventHandler(FrameworkElement_KeyDown);
}
}
}
private static void FrameworkElement_KeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Enter) {
DependencyObject o = sender as DependencyObject;
ICommand command = GetCommand(sender as DependencyObject);
FrameworkElement element = e.OriginalSource as FrameworkElement;
if (element != null) {
// If the command argument has been explicitly set (even to NULL)
if (GetHasCommandArgument(o)) {
object commandArgument = GetCommandArgument(o);
// Execute the command
if (command.CanExecute(commandArgument)) {
command.Execute(commandArgument);
}
}
else if (command.CanExecute(element.DataContext)) {
command.Execute(element.DataContext);
}
}
}
}
#endregion
}
MyUserControl.xaml
<UserControl
...
xmlns:b="clr-namespace:MyNameSpace.Interactivity"
...
<TextBox b:EnterKeyDown.Command="{Binding AddNewDetailCommand}"
b:EnterKeyDown.CommandArgument="{Binding Path=Text,RelativeSource={RelativeSource Self}}" />
...
</UserControl>
I ran into this same issue yesterday and solved it using custom triggers. It may seem a bit much at first, but I found this general pattern is usable for doing a lot of the things that I used to accomplish using event handlers directly in a view (like double click events). The first step is to create a trigger action that can accept a parameter since we will need it later.
public class ExecuteCommandAction : TriggerAction<FrameworkElement>
{
public string Command { get; set; }
protected override void Invoke(object o)
{
if (Command != null)
{
object ctx = AssociatedObject.DataContext;
if (ctx != null)
{
var cmd = ctx.GetType().GetProperty(Command)
.GetValue(ctx, null) as ICommand;
if (cmd != null && cmd.CanExecute(o))
{
cmd.Execute(o);
}
}
}
}
}
The next step is to create the trigger. You could do some interesting things with base classes to make it more generic for capturing different types of key presses, but we'll keep it simple.
public class TextBoxEnterKeyTrigger: TriggerBase<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.KeyUp += AssociatedObject_KeyUp;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.KeyUp -= AssociatedObject_KeyUp;
}
void AssociatedObject_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
TextBox textBox = AssociatedObject as TextBox;
object o = textBox == null ? null : textBox.Text;
if (o != null)
{
InvokeActions(o);
}
}
}
}
Keep in mind that even though you may have a data binding in place to your TextBox value, the property changed event won't fire because your textbox hasn't lost focus. For this reason I am passing the value of the TextBox.Text property to the command. The last step is to use this feature in your XAML. You need to be sure to include the Interactivity namespace as well as the namespace that contains your code from above.
<UserControl
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:common="clr-namespace:My.UI;assembly=My.UI">
<TextBox Text="{Binding Path=MyText, Mode=TwoWay}" IsEnabled="{Binding CanMyCommand}">
<i:Interaction.Triggers>
<common:TextBoxEnterKeyTrigger>
<common:ExecuteCommandAction Command=MyCommand" />
</common:TextBoxEnterKeyTrigger>
</i:Interaction.Triggers>
</TextBox>
</UserControl>
I used scottrudy's code in my application however, my textbox text is bound to some property in viewmodel class and this property is not getting updated by the time command is invoked after pressiong ENTER key because my textbox hasn't lost focus yet. So, to resolved this, i added the following code snippets just above InvokeActions(o) in AssociatedObject_KeyUp method and updated text property is getting updated in viewmodel class.
BindingExpression bindingExpression = (textBox).GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();
On top of my mind.. You can pass event args to command and than in ViewModel check if e.KeyPress = Keys.Enter.. this is not really code :) i dont have my VS on this computer.. this is rather an idea :)

Executing viewmodels command on enter in TextBox

I want to execute a command in my viewmodel when the user presses enter in a TextBox.
The command works when bound to a button.
<Button Content="Add" Command="{Binding Path=AddCommand}" />
But I can't bring it to work from the TextBox.
I tried an Inputbinding, but it didn't work.
<TextBox.InputBindings>
<KeyBinding Command="{Binding Path=AddCommand}" Key="Enter"/>
</TextBox.InputBindings>
I also tried to set the working button as default, but it doesn't get executed when enter is pressed.
Thanks for your help.
I know I am late to the party, but I got this to work for me. Try using Key="Return" instead of Key="Enter"
Here is the full example
<TextBox Text="{Binding FieldThatIAmBindingToo, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Command="{Binding AddCommand}" Key="Return" />
</TextBox.InputBindings>
</TextBox>
Make sure to use UpdateSourceTrigger=PropertyChanged in your binding, otherwise the property will not be updated until focus is lost, and pressing enter will not lose focus...
Hope this was helpful!
You have probably not made the command a property, but a field. It only works to bind to properties. Change your AddCommand to a property and it will work. (Your XAML works fine for me with a property instead of a field for the command -> no need for any code behind!)
Here's an attached dependency property I created for this. It has the advantage of ensuring that your text binding is updated back to the ViewModel before the command fires (useful for silverlight which doesn't support the property changed update source trigger).
public static class EnterKeyHelpers
{
public static ICommand GetEnterKeyCommand(DependencyObject target)
{
return (ICommand)target.GetValue(EnterKeyCommandProperty);
}
public static void SetEnterKeyCommand(DependencyObject target, ICommand value)
{
target.SetValue(EnterKeyCommandProperty, value);
}
public static readonly DependencyProperty EnterKeyCommandProperty =
DependencyProperty.RegisterAttached(
"EnterKeyCommand",
typeof(ICommand),
typeof(EnterKeyHelpers),
new PropertyMetadata(null, OnEnterKeyCommandChanged));
static void OnEnterKeyCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
ICommand command = (ICommand)e.NewValue;
FrameworkElement fe = (FrameworkElement)target;
Control control = (Control)target;
control.KeyDown += (s, args) =>
{
if (args.Key == Key.Enter)
{
// make sure the textbox binding updates its source first
BindingExpression b = control.GetBindingExpression(TextBox.TextProperty);
if (b != null)
{
b.UpdateSource();
}
command.Execute(null);
}
};
}
}
You use it like this:
<TextBox
Text="{Binding Answer, Mode=TwoWay}"
my:EnterKeyHelpers.EnterKeyCommand="{Binding SubmitAnswerCommand}"/>
You need to define Gesture instead of Key property of the KeyBinding:
<TextBox.InputBindings>
<KeyBinding Gesture="Enter" Command="{Binding AddCommand}"/>
</TextBox.InputBindings>
In addition to Mark Heath's answer, I took the class one step further by implementing Command Parameter attached property in this way;
public static class EnterKeyHelpers
{
public static ICommand GetEnterKeyCommand(DependencyObject target)
{
return (ICommand)target.GetValue(EnterKeyCommandProperty);
}
public static void SetEnterKeyCommand(DependencyObject target, ICommand value)
{
target.SetValue(EnterKeyCommandProperty, value);
}
public static readonly DependencyProperty EnterKeyCommandProperty =
DependencyProperty.RegisterAttached(
"EnterKeyCommand",
typeof(ICommand),
typeof(EnterKeyHelpers),
new PropertyMetadata(null, OnEnterKeyCommandChanged));
public static object GetEnterKeyCommandParam(DependencyObject target)
{
return (object)target.GetValue(EnterKeyCommandParamProperty);
}
public static void SetEnterKeyCommandParam(DependencyObject target, object value)
{
target.SetValue(EnterKeyCommandParamProperty, value);
}
public static readonly DependencyProperty EnterKeyCommandParamProperty =
DependencyProperty.RegisterAttached(
"EnterKeyCommandParam",
typeof(object),
typeof(EnterKeyHelpers),
new PropertyMetadata(null));
static void OnEnterKeyCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
ICommand command = (ICommand)e.NewValue;
Control control = (Control)target;
control.KeyDown += (s, args) =>
{
if (args.Key == Key.Enter)
{
// make sure the textbox binding updates its source first
BindingExpression b = control.GetBindingExpression(TextBox.TextProperty);
if (b != null)
{
b.UpdateSource();
}
object commandParameter = GetEnterKeyCommandParam(target);
command.Execute(commandParameter);
}
};
}
}
Usage:
<TextBox Text="{Binding Answer, Mode=TwoWay}"
my:EnterKeyHelpers.EnterKeyCommand="{Binding SubmitAnswerCommand}"
my:EnterKeyHelpers.EnterKeyCommandParam="your parameter"/>

Resources