Today's challenge is letting the Enter key update the value of a databound text box. I've cracked open Visual Studio 2008, started a new WPF application and in Window1.xaml

To illustrate we'll use a Border and a TextBox. The TextBox will set the Width of the Border by databinding to it. Here's the relevant XAML (layout goop omitted):
<TextBox
Text="{Binding ElementName=_sampleBorder,Path=Width,Mode=TwoWay}"
/>
<Border Name="_sampleBorder"
Background="Blue" Width="10" Height="50"
/>
ElementName
We want to bind to the Border, so we give it a name "_sampleBorder". This can be refered to in the Binding using the ElementName syntax.
Path
We want to specifically bind to the Width property on the Border.
Mode
We want changes in the TextBox.Text to update the Border.Width, and vice versa - if somehow the Border.Width gets changed we want that to be reflected in the TextBox. To accomplish this, we set the Mode to TwoWay.
Build and run... no worky!
The Border.Width (10) shows up in the TextBox.Text property but you can't change it!Why? The default databinding mode is to update on TextBox.LostFocus. From a user expierience, this typically means only the Tab key or clicking somewhere else would update the value. In this example, we don't have another control that takes focus, so the UI doesn't update.
UpdateSourceTrigger, not the solution
We can change how often the binding automatically updates using the UpdateSourceTrigger parameter. UpdateSourceTrigger by is set to LostFocus by default. Other options include updating 'Explicit'ly and on 'PropertyChanged'.
'PropertyChanged' is usually not a great option for a text box, as people frequently make typing mistakes and the feedback is often too distracting (for those of us who aren't too busy looking at the keyboard while they type.) ;-)
Having the LostFocus cake and updating 'Explicit'ly too
Since there's isn't a 'LostFocusOrEnterKeyPressed', I kept the default 'LostFocus' behavior and manually called UpdateSource on the binding when the enter key was pressed.
<TextBox
Text="{Binding ElementName=_sampleBorder,Path=Width,Mode=TwoWay}"
KeyDown="TextBox_KeyDown"
/>
// look for the enter key
private void TextBox_KeyDown(object sender, KeyEventArgs e) {
TextBox textBox = sender as TextBox;
if (e.Key == Key.Enter && textBox != null) {
// fish out <TextBox Text={Binding ... }" and update the source of the binding
BindingExpression binding = textBox.GetBindingExpression(TextBox.TextProperty);
if (binding != null) {
binding.UpdateSource();
}
}
}
If you have more than one textbox, this gets a bit tedious. Fortunately, using routed events, you can use the AddEventHandler syntax to get all the KeyDown events for all textboxes in your window.
public Window1() {
InitializeComponent();
this.AddHandler(TextBox.KeyDownEvent, new RoutedEventHandler(TextBox_KeyDown));
}
// look for the enter key
private void TextBox_KeyDown(object sender, RoutedEventArgs e) {
TextBox textBox = e.OriginalSource as TextBox;
KeyEventArgs args = e as KeyEventArgs;
if (args != null && args.Key == Key.Enter && textBox != null) {
// fish out <TextBox Text={Binding ... }" and update the source of the binding
BindingExpression binding = textBox.GetBindingExpression(TextBox.TextProperty);
if (binding != null) {
binding.UpdateSource();
}
}
}
There's more than one way to accomplish most things in WPF; you could also apply a style to the TextBoxes you want to have this behavior...
<Style TargetType="{x:Type TextBox}">
<EventSetter Event="KeyDown" Handler="TextBox_KeyDown"/>
</Style>
...or create a commanding solution, using FocusManager to get the focused TextBox...
... but the fundamental of calling binding.UpdateSource would likely re-appear in most solutions.
Source: [xaml cs ]
No comments:
Post a Comment