views:

256

answers:

3

Suppose you had a text box bound to a property of a data item, and a button. If you enter text in the text box, then click on the button with the mouse, the following events happen in this order:

  • The text is written from the control to the bound item
  • The button click event is fired

However, if you activate the button with a mnemonic key, the text box does not lose focus. It seems that the text is written from the control to the bound item only when the text box loses focus.

Is there a known workaround to this? I want the same behaviour whether you left-click on the button, tab to the button and press space, or use the mnemonic.

I'll provide a complete example. If you type in the word "Hello" and press the button, you get a message box "WidgetName=Hello". But if you then changed it to "Goodbye" and press ALT-A, it will still say "WidgetName=Hello".

Here's the XAML code

<Window x:Class="BindingOrder.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:BindingOrder"
    Title="Window1" Height="79" Width="282">
    <Window.Resources>
        <local:Widget x:Key="Widget" />
    </Window.Resources>
    <StackPanel Orientation="Horizontal" Height="30" VerticalAlignment="Top">
        <TextBox 
            Width="200" 
            Margin="3, 3, 3, 3" 
            Text="{Binding Source={StaticResource Widget}, Path=WidgetName}" />
        <Button  
            Click="OnApplyClicked" 
            Margin="3, 3, 3, 3">
            _Apply
        </Button>        
    </StackPanel>
</Window>

And the supporting code:

using System;
using System.Windows;

namespace BindingOrder
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void OnApplyClicked(object sender, RoutedEventArgs e)
        {
            Widget w = (Widget)this.Resources["Widget"];
            MessageBox.Show(string.Format("WidgetName={0}", w.WidgetName));
        }
    }

    public class Widget
    {
        public string WidgetName { get; set; }
    }
}
+1  A: 

You're right - the textbox doesn't update the binding until focus is lost by default. The activation by a mnemonic key isn't something that had occurred to me before :)

That's an unfortunate gotcha, but you could set the binding to update as the user is typing (see the UpdateSourceTrigger property).

Joseph
+1 because it is a solution. However, it's not really satisfactory because now the databinding will happen every time the user changes a single character. In my real application there is extra business logic that gets invoked in the setter, which I don't want to be continually running as the user is typing.
Andrew Shepherd
+1  A: 

The simplest solution I've found (other than using the UpdateSourceTrigger property, as Joseph suggested) is to shift focus to the button in its Click event. The button's Click event is fired no matter how the button is "clicked" (via keyboard or mouse).

If you want the TextBox to keep focus, you could keep hold of the currently-focused control in a variable, then shift focus to the button and immediately back to that control. Not ideal though, because the caret position in the TextBox will change.

Matt Hamilton
A: 

Once I explained the question in detail it becomes obvious.

  • When you click the button with the mouse the button receives the focus. This causes the TextBox to lose focus, which triggers the data binding action.
  • The button does not receive the focus when you use the mnemonic.

If the button receives the focus when you use the mnemonic the problem is solved.

So we add one line of code to ensure the button has the focus.

    private void OnApplyClicked(object sender, RoutedEventArgs e)
    {
        ((UIElement)sender).Focus();
        Widget w = (Widget)this.Resources["Widget"];
        MessageBox.Show(string.Format("WidgetName={0}", w.WidgetName));
    }
Andrew Shepherd