Question

It is convenient to have an "Accept Button" (in WPF: IsDefault="True") on a Form.

In the Windows Forms world, I used to read the data from the UI to the object(s) in the corresponding Click event of the button.

But with WPF, data binding ought to be used. In the constructor of the Window, I set this.DataContext = test;

And here comes the problem: the user entered some text in TextBox2, and hits the Enter key. Now, the command bound to the OK button gets executed, the data are saved.

But it is not the correct data! Why? TextBox2 has not yet lost focus, and consequently the ViewModel has not yet been updated. Changing the UpdateSourceTrigger to PropertyChanged is not always appropriate (e.g. formatted numbers), I am looking for a general solution.

How do you overcome such a problem?

Was it helpful?

Solution

Typically I use a custom Attached Property to tell WPF to update the binding source when the Enter key is pressed

It is used in the XAML like this:

<TextBox Text="{Binding SomeProperty}" 
         local:TextBoxProperties.EnterUpdatesTextSource="True" />

And the code for the attached property is below:

public class TextBoxProperties
{
    // When set to True, Enter Key will update Source
    public static readonly DependencyProperty EnterUpdatesTextSourceProperty =
        DependencyProperty.RegisterAttached("EnterUpdatesTextSource", typeof(bool),
                                            typeof(TextBoxProperties),
                                            new PropertyMetadata(false, EnterUpdatesTextSourcePropertyChanged));

    // Get
    public static bool GetEnterUpdatesTextSource(DependencyObject obj)
    {
        return (bool)obj.GetValue(EnterUpdatesTextSourceProperty);
    }

    // Set
    public static void SetEnterUpdatesTextSource(DependencyObject obj, bool value)
    {
        obj.SetValue(EnterUpdatesTextSourceProperty, value);
    }

    // Changed Event - Attach PreviewKeyDown handler
    private static void EnterUpdatesTextSourcePropertyChanged(DependencyObject obj,
                                                              DependencyPropertyChangedEventArgs e)
    {
        var sender = obj as UIElement;
        if (obj != null)
        {
            if ((bool)e.NewValue)
            {
                sender.PreviewKeyDown += OnPreviewKeyDownUpdateSourceIfEnter;
            }
            else
            {
                sender.PreviewKeyDown -= OnPreviewKeyDownUpdateSourceIfEnter;
            }
        }
    }

    // If key being pressed is the Enter key, and EnterUpdatesTextSource is set to true, then update source for Text property
    private static void OnPreviewKeyDownUpdateSourceIfEnter(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            if (GetEnterUpdatesTextSource((DependencyObject)sender))
            {
                var obj = sender as UIElement;
                BindingExpression textBinding = BindingOperations.GetBindingExpression(
                    obj, TextBox.TextProperty);

                if (textBinding != null)
                    textBinding.UpdateSource();
            }
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top