Поделиться через


UpdateSourceTrigger on Windows Phone data bindings

I am writing this tiny demo app, that has a TextBox data bound to a ViewModel. 
I want the TextBox to fire notifications to the ViewModel whenever the text changes (as opposed to only firing notifications when the textbox loses focus).
In WPF, this is trivial to do, you just set the UpdateSourceTrigger on the Binding to PropertyChanged  (in fact, I think that is the default).
On the phone, I only see UpdateSourceTrigger supporting:

  • Default (which for TextBox appears to be LostFocus ) and
  • Explicit.  Which is just telling me to man-up and do it on my own logic.
    No option for PropertyChanged.

What to do?  
[12/5 (Update part 1) -- Updating this post due to enough feedback that the semantic of TextChanged is better than my post’s KeyUp.
I did try that before suggesting KeyUp on my original post but I was seeing TextChanged fire more often than KeyUp (aka more times than I felt necessary).
Now that I have seen there is no big perf hit (since others are doing it with TextChanged) I am back to proper semantics.
Also you made me second guess and I tested on a keyboard and noticed that arrows can even the score firing KeyUp events. 

How about:

  1. Setting the UpdateSourceTrigger to Explicit 
  2. Listening to TextBox TextChanged event, then firing the explicit update?

Here are the snippets:
In my XAML,

    1: <TextBox x:Name="empIdTextBox" Text="{Binding Id, Mode=TwoWay, UpdateSourceTrigger=Explicit}"                    
    2:             TextChanged="empIdTextBox_TextChanged"/>

code-behind,

    1: private void empIdTextBox_TextChanged(object sender, TextChangedEventArgs e)
    2:     {     
    3:       TextBox box = (TextBox)sender;
    4:       BindingExpression be = box.GetBindingExpression(TextBox.TextProperty);
    5:       be.UpdateSource();
    6:  
    7:     }

 

 

[12/5 Update part 2]

Corrado Cavalli and Curt Nicholas emphasize that I should use Behaviors.  

My sample was part of the intro to data binding chapter in Learning Windows Phone, so I did not want to go to behaviors (yet). Still, for production apps, here is the behavior.

Curt also mentions I can cache the BindingExpression. I tested it and seems to work, but I was not sure when (or if) binding expression gets invalidated so I leave it for you to complete the research for your scenario.

    1: public class UpdateSourceOnTextChangedBehavior : Behavior<TextBox>
    2:   {
    3:  
    4:     protected override void OnAttached()
    5:     {
    6:       base.OnAttached();
    7:       this.AssociatedObject.TextChanged += this.OnTextChanged;
    8:     }
    9:  
   10:     private void OnTextChanged(object sender, TextChangedEventArgs e)
   11:     {
   12:       BindingExpression be =
   13:         this.AssociatedObject.GetBindingExpression(TextBox.TextProperty);
   14:       be.UpdateSource(); 
   15:     }
   16:  
   17:     protected override void OnDetaching()
   18:     {
   19:       base.OnDetaching();
   20:       this.AssociatedObject.TextChanged -= this.OnTextChanged;
   21:     }
   22:   }
    1: <TextBox x:Name="empIdTextBox" Grid.Column="1"                 
    2:                 Text="{Binding Id, Mode=TwoWay,UpdateSourceTrigger=Explicit}" > 
    3:          <interactivity:Interaction.Behaviors>
    4:            <local:UpdateSourceOnTextChangedBehavior />                             
    5:          </interactivity:Interaction.Behaviors>          
    6:        </TextBox>         

Happy Windows Phone coding!

Comments

  • Anonymous
    December 05, 2010
    Hey Jaime, have you tried using the TextChanged event? That more literally matches your stated requirement. :) As for perf, you might retrieve the binding expression just once rather than with every change, assuming that fits your needs. I use a behavior that I built for PasswordBox that does pretty much the same thing.

  • Anonymous
    December 05, 2010
    You need to use  a trigger and so bind the Update Source functionality to any event you want

  • Anonymous
    December 05, 2010
    You need to use  a trigger and so bind the Update Source functionality to any event you want

  • Anonymous
    December 05, 2010
    Jaime, I didn't mean to suggest that you should be using a behavior--you did write "tiny demo app," after all. I did mean to suggest that the path you're heading down should work for you. :) Re .TextChanged firing more often than expected, possibly a bug?

  • Anonymous
    December 12, 2010
    The Behavior is so clean. It is mostly just "be.UpdateSource();". If you do another update perhaps you can show people how simple it is to consume the Behavior using Expression Blend :)