How to Invoke a Command on the ViewModel by Pressing the Enter Key in a TextBox with Silverlight and MVVM
I recently attempted to upgrade a WPF application to compile for Silverlight as well. One of the many issues I ran into was that in the WPF version, pressing the Enter key while I was in a TextBox
caused the OK button I had on the form to be clicked by virtue of the fact that I could set the IsDefault property on the button. However, after porting to Silverlight, that no longer worked, since the IsDefault
property is missing..
A web-search revealed that someone had made a “behavior” that allows a specified button to be clicked when you press Enter within a TextBox
(available for download here). However, it had one big problem: at the point that the command fired in my ViewModel
, the value bound to the textbox contents had not been updated, since the textbox had not lost focus.
The original WPF binding I had an UpdateSourceTrigger
ensuring that the ViewModel
was always kept up to date with what was in the TextBox
:
<TextBox Text="{Binding Answer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
but in Silverlight, the PropertyChanged
UpdateSourceTrigger
is not available, so I was left with the following:
<TextBox Text="{Binding Answer, Mode=TwoWay}" />
I decided to make my own EnterKeyCommand
binding that would allow you to specify for a TextBox
which command on the ViewModel
should be run. Here’s the code:
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);
}
};
}
}
Most of it is pretty simple, and it will allow an Enter key command to be specified for any control, not just textboxes. However, if you have bound to a textbox, it will call UpdateSource
on any Text
binding you have made, to ensure your ViewModel
operates on the latest data.
Here’s how you use it in XAML:
<TextBox
Text="{Binding Answer, Mode=TwoWay}"
my:EnterKeyHelpers.EnterKeyCommand="{Binding SubmitAnswerCommand}"/>
It also has the advantage of being considerably more succinct than the equivalent XAML for using the behavior I linked to earlier.
Comments
Be careful using that inside ItemsControls as that will be a memory leak. The += on the KeyDown event is never cleaned up.
blorqthanks blorq. Would be interested if you have an idea for what would be a safer way.
Mark HI think I need to update it with unsubscription because I got a strange case recently where OnEnterKeyCommandChanged seemed to get called twice on the same object, meaning I double-subscribed to the key-down event.
This shouldn't cause a memory leak of the textbox, the behavior/event handler is a static class. In the reverse,where a class instance subscribes to a static event or a event of a long-lived object, you may end up with 'leaks'
AnonymousHere's another take on this that avoids the leak (you can set the attached property to null to clear it).
Drew Noakes