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

Share this post

Comments (5)

  • Karl Shifflett Reply

    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

    May 3, 2008 at 11:43 am
  • hubethom Reply

    Hi Karl,

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

    Thanks a lot for your code snippet.

    May 6, 2008 at 7:47 am
  • Sam Goldberg Reply

    Anyway to do the same thing in .NET 2.0?

    January 27, 2009 at 8:24 pm
  • Fawad Moon Reply

    I think following solution will also work.

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

    January 28, 2009 at 8:23 am
  • Mike Reply

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

    April 12, 2009 at 11:50 pm

Leave a Reply to Mike Cancel reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.