122

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 to make the databinding happen when I press the 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.

akjoshi
  • 15,374
  • 13
  • 103
  • 121
Jobi Joy
  • 49,102
  • 20
  • 108
  • 119

12 Answers12

151

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.UpdatePropertySourceWhenEnterPressed="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).

Grhm
  • 6,726
  • 4
  • 40
  • 64
Samuel Jack
  • 32,712
  • 16
  • 118
  • 155
  • i tried but it seems webbrowser doesnt update its source. anything i missed? i didnt handle any event. i assume by binding 2 way, its auto handle event. am i wrong? thanks. – Syaiful Nizam Yahya Jan 12 '13 at 18:32
  • 11
    In an effort to save future readers a few minutes of fiddling, I just used this in my project, and the sample XAML provided above didn't work right. The source was updated on every character change. Changing "UpdateSourceTrigger=PropertyChanged" to "UpdateSourceTrigger=Explicit" fixed the issue. Now it all works as desired. – ihake Jun 18 '14 at 16:51
  • 1
    @ihake: I think your recommended change will also prevent updating on focus lost though – VoteCoffee Nov 13 '14 at 12:53
  • 6
    UpdateSourceTrigger=PropertyChanged may be inconvenient when typing a real number. For example "3." causes a problem when bound to a float. I'd recommend not specifying UpdateSourceTrigger (or setting to LostFocus) in addition to the attached behaviour. This gives the best of both worlds. – David Hollinshead Jul 20 '15 at 10:26
  • 3
    Excellent work! Tiny nit-pick: When the `UpdatePropertySourceWhenEnterPressed` changes from a valid value to a different valid value, you are unsubscribing & re-subscribing to the `PreviewKeyDown` event unnecessarily. Instead, all you should need is to check whether or not the `e.NewValue` is `null` or not. If not `null`, then subscribe; otherwise, if `null`, then unsubscribe. – Nicholas Miller Dec 02 '15 at 18:48
  • Another thing, I've noticed that calling `binding.UpdateSource()` clears validation errors. I needed to check first if the update should occur by looking at `BindingExpression.HasError`. – Nicholas Miller Dec 02 '15 at 20:04
  • 1
    I love this solution, thanks for posting it! For anyone who needs to attach this behaviour to numerous TextBoxes in your application, I posted an extension to this answer on how you can achieve that very easily. [See the post here](https://stackoverflow.com/a/49062394/7159784) – Nik Mar 02 '18 at 03:31
67

This is how I solved this problem. I created a special event handler that went into the code behind:

private void TextBox_KeyEnterUpdate(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        TextBox tBox = (TextBox)sender;
        DependencyProperty prop = TextBox.TextProperty;

        BindingExpression binding = BindingOperations.GetBindingExpression(tBox, prop);
        if (binding != null) { binding.UpdateSource(); }
    }
}

Then I just added this as a KeyUp event handler in the XAML:

<TextBox Text="{Binding TextValue1}" KeyUp="TextBox_KeyEnterUpdate" />
<TextBox Text="{Binding TextValue2}" KeyUp="TextBox_KeyEnterUpdate" />

The event handler uses its sender reference to cause it's own binding to get updated. Since the event handler is self-contained then it should work in a complex DataTemplate. This one event handler can now be added to all the textboxes that need this feature.

Ben
  • 3,241
  • 4
  • 35
  • 49
  • 10
    Something tells me this is an underrated post. A lot of answers are popular because they are "XAML-y". But this one appears to save space in most occasions. – j riv Jun 05 '15 at 23:09
  • 4
    +1 This also allows you to leave alone UpdateSourceTrigger in case you have already painstakingly got your TextBox bindings to already behave the way you want (with styles, validation, twoway, etc.), but currently just won't receive input after pressing Enter. – Jamin Aug 19 '15 at 22:04
  • 2
    This is the cleanest approach that I've found, and in my opinion should be marked as the answer. Added an additional null check on sender, a check on Key.Return (Enter key on my keyboard returns Key.Return), and Keyboard.ClearFocus() to remove focus from the TextBox after updating the source property. I've made edits to the answer that's awaiting peer review. – Oystein Oct 27 '17 at 11:16
  • 2
    Agree with comments above. But for me KeyDown event was more appropriate – Allender Feb 21 '19 at 10:44
  • 1
    I used `KeyBinding` in the XAML to fire this method because my UI has a "Default" control that catches the enter key. You need to catch it within this TextBox to stop it propagating up the UI tree to the "Default" control. – CAD bloke Jun 11 '19 at 02:56
  • NOTE for UWP projects: the binding has be obtained from the textbox itself, `tBox.GetBindingExpression(props)` because for some reason they left out the static method. – Ben Jun 19 '19 at 19:23
  • 1
    This works well in a dialog situation when a button has `IsDefault` property set to true. – hfann Oct 30 '20 at 22:24
  • This could also be even simpler if you had a default style for TextBox with an EventSetter that directed all KeyUp events (or KeyDown if you prefer) to the handler that's listed. If the style were in a ResourceDictionary, you could just make some code-behind for the dictionary. Then you would not even need to specify the KeyUp/KeyDown handler on each TextBox – Joe Feb 04 '21 at 02:03
47

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
  • 200,371
  • 61
  • 386
  • 320
  • 3
    This answer may be simpler than the one marked as answer, but it has some limitations. Image you want to do some kind of check in the set-function of the bound property (checking if the input is valid). You do not want to call that check function after every key the user pressed, do you (especially not when the function takes some time).? – sth_Weird Dec 11 '15 at 07:40
25

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
  • 6,417
  • 3
  • 30
  • 31
  • 9
    In WPF/Silverlight you should never use inheritance - it messes styles and is not as flexible as Attached Behaviors. For example with Attached Behaviors you can have both Watermark and UpdateOnEnter on the same textbox. – Mikhail Poda Jan 16 '12 at 12:57
16

If you combine both Ben and ausadmin's solutions, you end up with a very MVVM friendly solution:

<TextBox Text="{Binding Txt1, Mode=TwoWay, UpdateSourceTrigger=Explicit}">
    <TextBox.InputBindings>
        <KeyBinding Gesture="Enter" 
                    Command="{Binding UpdateTextBoxBindingOnEnterCommand}"
                    CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
    </TextBox.InputBindings>
</TextBox>

...which means you are passing the TextBox itself as the parameter to the Command.

This leads to your Command looking like this (if you're using a DelegateCommand-style implementation in your VM):

    public bool CanExecuteUpdateTextBoxBindingOnEnterCommand(object parameter)
    {
        return true;
    }

    public void ExecuteUpdateTextBoxBindingOnEnterCommand(object parameter)
    {
        TextBox tBox = parameter as TextBox;
        if (tBox != null)
        {
            DependencyProperty prop = TextBox.TextProperty;
            BindingExpression binding = BindingOperations.GetBindingExpression(tBox, prop);
            if (binding != null) 
                binding.UpdateSource();
        }
    }

This Command implementation can be used for any TextBox and best of all no code in the code-behind though you may want to put this in it's own class so there are no dependencies on System.Windows.Controls in your VM. It depends on how strict your code guidelines are.

toadflakz
  • 7,764
  • 1
  • 27
  • 40
  • 1
    Nice. Very clean. If a multibinding is involved, you may need to use BindingOperations.GetBindingExpressionBase(tBox, prop); and then binding.UpdateTarget(); – VoteCoffee Nov 13 '14 at 13:35
5

Here is an approach that to me seems quite straightforward, and easier that adding an AttachedBehaviour (which is also a valid solution). We use the default UpdateSourceTrigger (LostFocus for TextBox), and then add an InputBinding to the Enter Key, bound to a command.

The xaml is as follows

       <TextBox Grid.Row="0" Text="{Binding Txt1}" Height="30" Width="150">
        <TextBox.InputBindings>
            <KeyBinding Gesture="Enter" 
                        Command="{Binding UpdateText1Command}"
                        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}},Path=Text}" />
        </TextBox.InputBindings>
    </TextBox>

Then the Command methods are

Private Function CanExecuteUpdateText1(ByVal param As Object) As Boolean
    Return True
End Function
Private Sub ExecuteUpdateText1(ByVal param As Object)

    If TypeOf param Is String Then
        Txt1 = CType(param, String)
    End If
End Sub

And the TextBox is bound to the Property

 Public Property Txt1 As String
    Get
        Return _txt1
    End Get
    Set(value As String)
        _txt1 = value
        OnPropertyChanged("Txt1")
    End Set
End Property

So far this seems to work well and catches the Enter Key event in the TextBox.

ausadmin
  • 1,628
  • 1
  • 12
  • 5
5

This is not an answer to the original question, but rather an extension of the accepted answer by @Samuel Jack. I did the following in my own application, and was in awe of the elegance of Samuel's solution. It is very clean, and very reusable, as it can be used on any control, not just the TextBox. I thought this should be shared with the community.

If you have a Window with a thousand TextBoxes that all require to update the Binding Source on Enter, you can attach this behaviour to all of them by including the XAML below into your Window Resources rather than attaching it to each TextBox. First you must implement the attached behaviour as per Samuel's post, of course.

<Window.Resources>
    <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
        <Style.Setters>
            <Setter Property="b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed" Value="TextBox.Text"/>
        </Style.Setters>
    </Style>
</Window.Resources>

You can always limit the scope, if needed, by putting the Style into the Resources of one of the Window's child elements (i.e. a Grid) that contains the target TextBoxes.

Nik
  • 1,780
  • 1
  • 14
  • 23
3

This works for me:

        <TextBox                 
            Text="{Binding Path=UserInput, UpdateSourceTrigger=PropertyChanged}">
            <TextBox.InputBindings>
                <KeyBinding Key="Return" 
                            Command="{Binding Ok}"/>
            </TextBox.InputBindings>
        </TextBox>
Valery
  • 39
  • 1
2

In case you are using MultiBinding with your TextBox you need to use BindingOperations.GetMultiBindingExpression method instead of BindingOperations.GetBindingExpression.

// Get the correct binding expression based on type of binding
//(simple binding or multi binding.
BindingExpressionBase binding = 
  BindingOperations.GetBindingExpression(element, prop);
if (binding == null)
{
    binding = BindingOperations.GetMultiBindingExpression(element, prop);
}

if (binding != null)
{
     object value = element.GetValue(prop);
     if (string.IsNullOrEmpty(value.ToString()) == true)
     {
         binding.UpdateTarget();
     }
     else
     {
          binding.UpdateSource();
     }
}
akjoshi
  • 15,374
  • 13
  • 103
  • 121
1

Answered here quite elegantly using attached behaviors, my preferred method for almost anything.

WPF how to make textbox lose focus after hitting enter

Community
  • 1
  • 1
NielW
  • 3,626
  • 1
  • 30
  • 38
1

I personally think having a Markup Extension is a cleaner approach.

public class UpdatePropertySourceWhenEnterPressedExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return new DelegateCommand<TextBox>(textbox => textbox.GetBindingExpression(TextBox.TextProperty).UpdateSource());
    }
}


<TextBox x:Name="TextBox"
             Text="{Binding Text}">
        <TextBox.InputBindings>
            <KeyBinding Key="Enter"
                        Command="{markupExtensions:UpdatePropertySourceWhenEnterPressed}" 
                        CommandParameter="{Binding ElementName=TextBox}"/>
        </TextBox.InputBindings>
</TextBox>
metoyou
  • 639
  • 1
  • 7
  • 22
0

A different solution (not using xaml but still quite clean I think).

class ReturnKeyTextBox : TextBox
{
    protected override void OnKeyUp(KeyEventArgs e)
    {
        base.OnKeyUp(e);
        if (e.Key == Key.Return)
            GetBindingExpression(TextProperty).UpdateSource();
    }
}
Hauk
  • 1