LostFocus (TextBox) vs. Buttons IsDefault-Property

If you bind the Text-Property of a TextBox to something, the "something" is updated when the TextBox loses focus. This is the Default-UpdateSourceTrigger defined in the Metadata for the TextBox.TextProperty. In a Data Binding you can specify another UpdateSourceTrigger, like e.g. PropertyChanged.

If a TextBox has LostFocus as UpdateSourceTrigger, which is the default, you can get problems if the TextBox is inside of a Dialog that contains a OK-Button with the IsDefault-Property set to true. When the TextBox has the Focus, the user can press Enter and the Button is clicked, but the TextBox still keeps the Focus. The LostFocus-Event doesn’t occur, so the Source of a Data Binding isn’t updated with the value of the TextBoxs Text-Property. You need to update the source explicit. Let’s take an example and use the Person-class of my previous post:

public class Person : INotifyPropertyChanged
{
  private string _name;
  public string Name
  {
    get { return _name; }
    set
    {
      _name = value;
      if (PropertyChanged != null)
        PropertyChanged(this,
          new PropertyChangedEventArgs("Name"));
    }
  }
  public event PropertyChangedEventHandler PropertyChanged;
}

The Window below just contains a TextBox and a Button. The Text-Property of the TextBox is bound to the Name of the data in the DataContext. This would be a Person-instance, as you’ll see below in the codebehind-file. The Button has it’s IsDefault-Property set to true and defines an eventhandler for the Click-Event.

<Window x:Class="LostFocusTest.Window1" ...
  xmlns:local="clr-namespace:LostFocusTest"
  Title="Focus/IsDefault" Width="200" SizeToContent="Height">
  <StackPanel>
    <TextBox Margin="5" Text="{Binding Name}"/>
    <Button Content="OK" IsDefault="True"
       Click="Button_Click"/>
  </StackPanel>
</Window>

In the Constructor in the Codebehind-file a Person-instance is created. It’s assigned to the Windows DataContext-Property, so the TextBox above will display the Persons name, which is set to "Thomas". The Click-Eventhandler for the Button simply displays the name of the Person in a MessageBox.

public partial class Window1 : Window
{
  private Person _pers;
  public Window1()
  {
    InitializeComponent();
    _pers = new Person();
    _pers.Name = "Thomas";
    this.DataContext = _pers;
  }

  private void Button_Click(object sender, RoutedEventArgs e)
  {
    MessageBox.Show(_pers.Name);
  }
}

If I start the application and type "Urs" into the TextBox and press Enter, the Button is clicked but the TextBox keeps the focus. So the Source, which is the Person-Instance, isn’t updated and the MessageBox still displays the old value (“Thomas”), as you can see in the Image below:

LostFocus vs. IsDefault

For such a scenario, you have to update the source explicit. You could do this in the Click-Eventhandler. To update the source, you have to get the BindingExpression for the TextProperty. You get the BindingExpression by calling the GetBindingExpression-Method on the TextBox. Pass in the Text-Property as parameter. On the BindingExpression call the UpdateSource-Method to update the Person-instance explicitly with the value defined in the Text-Property of the TextBox. With the Eventhandler below the Person-Instance would always be actualized when the Button is clicked. The MessageBox would display the right value:

private void Button_Click(object sender, RoutedEventArgs e)
{
  TextBox textBox = Keyboard.FocusedElement as TextBox;
  if (textBox != null)
  {
    BindingExpression be =
      textBox.GetBindingExpression(TextBox.TextProperty);
    if (be != null)
      be.UpdateSource();
  }
  MessageBox.Show(_pers.Name);
}

Just kick it for me:

kick it on DotNetKicks.com

Tags: , ,

5 Responses to “LostFocus (TextBox) vs. Buttons IsDefault-Property”

  1. Karl Shifflett Says:

    Thomas,

    Nice work! Yep, I ran into this monster too!

    In Part 2 of my WPF series: http://www.codeproject.com/KB/WPF/WPFBusinessAppsPartTwo.aspx I ran into this and it drover me nuts until I figured out what was up.

    Below is a code snippet from the article. The UpdateFocusedField method is actually place in the base class of the UserControl that hosts all my forms.

    Private Sub btnSave_Click(ByVal sender As Object, _
    ByVal e As System.Windows.RoutedEventArgs) _
    Handles btnSave.Click

    UpdateFocusedField()

    If _objCustomer.IsValid Then
    Me.frmNotification.NotificationMessage = “Customer record saved”
    End If

    End Sub

    Private Sub UpdateFocusedField()

    Dim fwE As FrameworkElement = _
    TryCast(Keyboard.FocusedElement, FrameworkElement)

    If fwE IsNot Nothing Then

    Dim expression As BindingExpression = Nothing

    If TypeOf fwE Is TextBox Then
    expression = fwE.GetBindingExpression(TextBox.TextProperty)
    ‘TODO add more controls types here. Won’t be that many.
    End If

    If expression IsNot Nothing Then
    expression.UpdateSource()
    End If

    End If

    End Sub

    Cheers,

    Karl

  2. hubethom Says:

    Hi Karl,

    yes, it’s a real monster. But the monster becomes your best friend with bindingexpression. :-)

    Thanks a lot for your code snippet.

  3. Sam Goldberg Says:

    Anyway to do the same thing in .NET 2.0?

  4. Fawad Moon Says:

    I think following solution will also work.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
    ((Button)sender).Focus();
    MessageBox.Show(_pers.Name);
    }

  5. Mike Says:

    Thanks for posting this snippet this is exactly what I needed.

Leave a Reply