Keeping it focused

Well, it’s been a while since I’ve blogged, as I’ve been a busy little bee working on CodeStash. I feel guilty about this, so I’d like to take the chance to pass on the solution to a problem that was posted on Code Project today. The scenario goes like this:

You have a ViewModel with some text that you want to be updated, and you do all the usual binding work with some TextBox elements. The thing is, you want to update your ViewModel properties when the TextBox loses focus. OK, so that’s easily achieved – in fact, it’s the default behaviour of the TextBox. However, you have a button with IsDefault set on it, so pressing Enter in the TextBox triggers the button click – but as the TextBox hasn’t lost focus, the property you are binding to doesn’t get updated. This is, of course, a problem. Fortunately, there’s an easy little trick that you can deploy to update the property, and it’s easily achieved using an attached behavior.

All you need to do is associate the behaviour to the Button you have set the IsDefault property on, and Bob is your mothers brother, the Text property updates. So, what does this behavior look like:

using System.Windows.Interactivity;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
using System.Windows.Data;
/// <summary>
/// Associate this behaviour with the button that you mark as IsDefault
/// to trigger the ViewModel update when the user clicks enter in a textbox
/// and the property doesn't update because the update source is set to
/// lost focus.
/// </summary>
public class DefaultButtonUpdateTextBoxBindingBehavior : Behavior<Button>
{
    /// <summary>
    /// Hook into the button click event.
    /// </summary>
    protected override void OnAttached()
    {
        AssociatedObject.Click += AssociatedObject_Click;
        base.OnAttached();
    }

    /// <summary>
    /// Unhook the button click event.
    /// </summary>
    protected override void OnDetaching()
    {
        AssociatedObject.Click -= AssociatedObject_Click;
    }

    /// <summary>
    /// The click event handler.
    /// </summary>
    void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        // Get the element with the keyboard focus
        FrameworkElement el = Keyboard.FocusedElement as FrameworkElement;
        if (el != null && el is TextBox)
        {
            // Get the binding expression associated with the text property
            // for this element.
            BindingExpression expression = el.GetBindingExpression(TextBox.TextProperty);
            if (expression != null)
            {
                // Now, trigger the update.
                expression.UpdateSource();
            }
        }
    }
}

As you can see, there’s not that much code needed. Basically, we get the element that has focus, and retrieve the binding expression associated with it. Once you get the binding expression, we trigger the update.

I’ve copied this behaviour into CodeStash – the handy repository that I will be putting future snippets in for your delectation.

5 thoughts on “Keeping it focused

  1. Sesharaman

    From whatever little I read and understood/misunderstood?, the problem of updating a TextBox in viewmodel is done by me using a variable bound to the said TextBox, in ViewModel which is implementing INotifyPropertyChanged.

    1. peteohanlon

      Sesharaman – the issue is a very specific one here. In general, you are right, but there is a scenario whereby you have decided that you want to update your property on the LostFocus event, and not PropertyChanged. Now, if you move your cursor out of this field, the LostFocus will happen and the property will be updated. That’s all well and good.

      Now, suppose that you also have a button on this form and this button has the IsDefault property set to true. Go back into the TextBox and make some changes, then press Enter – the TextBox doesn’t lose focus, but the default button behaviour is triggered; resulting in the property not being updated. That’s what this code does – it sorts that out. Just attach this behaviour to the button, and it triggers the update on the focused element.

      I hope that it is clear now.

Leave a comment