tags:

views:

4986

answers:

3

The default DataBinding on TextBox is TwoWay and it commits the text to the Property only when TextBox lost its focus. Is there any easy XAML way exists to make the DataBinding Happens when I press Enter Key on the TextBox?. I know it is pretty easy to do in the code behind, but imagine if this textBox is inside some complex DataTemplate.

+3  A: 

I don't believe that there's any "pure XAML" way to do what you're describing. You can set up a binding so that it updates whenever the text in a TextBox changes (rather than when the TextBox loses focus) by setting the UpdateSourceTrigger property, like this:

<TextBox Name="itemNameTextBox"
    Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" />

If you set UpdateSourceTrigger to "Explicit" and then handled the TextBox's PreviewKeyDown event (looking for the Enter key) then you could achieve what you want, but it would require code-behind. Perhaps some sort of attached property (similar to my EnterKeyTraversal property) woudld work for you.

Matt Hamilton
Thank you for your simple solution to this, works perfectly.
Jim Beam
+10  A: 

You can make yourself a pure XAML approach by creating an attached behaviour.

Something like this:

public static class InputBindingsManager
    {

        public static readonly DependencyProperty UpdatePropertySourceWhenEnterPressedProperty = DependencyProperty.RegisterAttached(
            "UpdatePropertySourceWhenEnterPressed", typeof(DependencyProperty), typeof(InputBindingsManager), new PropertyMetadata(null, OnUpdatePropertySourceWhenEnterPressedPropertyChanged));

        static InputBindingsManager()
        {

        }

        public static void SetUpdatePropertySourceWhenEnterPressed(DependencyObject dp, DependencyProperty value)
        {
            dp.SetValue(UpdatePropertySourceWhenEnterPressedProperty, value);
        }

        public static DependencyProperty GetUpdatePropertySourceWhenEnterPressed(DependencyObject dp)
        {
            return (DependencyProperty)dp.GetValue(UpdatePropertySourceWhenEnterPressedProperty);
        }

        private static void OnUpdatePropertySourceWhenEnterPressedPropertyChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = dp as UIElement;

            if (element == null)
            {
                return;
            }

            if (e.OldValue != null)
            {
                element.PreviewKeyDown -= HandlePreviewKeyDown;
            }

            if (e.NewValue != null)
            {
                element.PreviewKeyDown += new KeyEventHandler(HandlePreviewKeyDown);
            }
        }

        static void HandlePreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                DoUpdateSource(e.Source);
            }
        }

        static void DoUpdateSource(object source)
        {
            DependencyProperty property =
                GetUpdatePropertySourceWhenEnterPressed(source as DependencyObject);

            if (property == null)
            {
                return;
            }

            UIElement elt = source as UIElement;

            if (elt == null)
            {
                return;
            }

            BindingExpression binding = BindingOperations.GetBindingExpression(elt, property);

            if (binding != null)
            {
                binding.UpdateSource();
            }
        }
    }

Then in your XAML you set the InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty property to the one you want updating when the Enter key is pressed. Like this

<TextBox Name="itemNameTextBox"
Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" b:InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty="TextBox.Text"/>

(You just need to make sure to include an xmlns clr-namespace reference for "b" in the root element of your XAML file pointing to which ever namespace you put the InputBindingsManager in).

Samuel Jack
There's a small bug in the above code that took me a bit to figure out. Make sure that the Get and Set functions have the same name as the dependency property. They're both missing the final "Property" in their names.
Scott Bilas
@Scott, Thanks for picking that up. The bug is actually that the name of the property supplied as the first parameter to RegisterAttached shouldn't have 'Property' at the end.
Samuel Jack
mate, it'd be great if you could actually update your code then :) Great answer!
flq
@Frank: Fixed! Thanks for the prompt.
Samuel Jack
You saved me a headache and a bunch of unmaintainable code in my code-behind. Great answer, Thanks!
Mendelt
+4  A: 

You could easily create your own control inheriting from TextBox and reuse it throughout your project.

Something similar to this should work:

public class SubmitTextBox : TextBox
{
    public SubmitTextBox()
        : base()
    {
        PreviewKeyDown += new KeyEventHandler(SubmitTextBox_PreviewKeyDown);
    }

    void SubmitTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            BindingExpression be = GetBindingExpression(TextBox.TextProperty);
            if (be != null)
            {
                be.UpdateSource();
            }
        }
    }
}

There may be a way to get around this step, but otherwise you should bind like this (using Explicit):

<custom:SubmitTextBox
    Text="{Binding Path=BoundProperty, UpdateSourceTrigger=Explicit}" />
Ryan Versaw