Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM Light is too fast :)

I have a simple WM7 Page with a TextBox. Futher, I assigned EventToCommand (a RelayCommand<string>) to this TextBox, reacting to the TextChanged event. For testing pourposes I made additional method TextBox_TextChanged in the page's code behind. Both the command and TextBox_TextChanged print a message box with the textbox content.

Initial value of the TextBox is "ABC". Then I press D and:

  1. TextBox_TextChanged prints ABCD.
  2. The command prints ABC. D is missing.

Why is the command so fast?

Command declaration:

public RelayCommand<string> TextChanged {get; private set;}

Command initialization:

TextChanged = new RelayCommand<string>((s) => MessageBox.Show(s));

Command binding:

<TextBox x:Name="SearchTextBox" Margin="10,0" TextWrapping="Wrap" Text="{Binding SearchString, Mode=TwoWay}" FontStyle="Italic" TextChanged="SearchTextBox_TextChanged" >
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="TextChanged">
            <GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding TextChanged, Mode=OneWay}" CommandParameter="{Binding Text, ElementName=SearchTextBox}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>
like image 558
Hikari Avatar asked Feb 27 '23 02:02

Hikari


1 Answers

I can't reproduce this behaviour. I have tried using EventToCommand and a Behaviour(which simply listens to TextChanged event).

Without seeing the code I suspect this might be to do with how you get the text of the search box or a logic error elsewhere.

This is a snippet of how I use EventToCommand:

<TextBox Name="SearchTextBox">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="TextChanged">
      <cmd:EventToCommand Command="{Binding TestTextChangedCommand,Mode=OneWay}" CommandParameter="{Binding Path=Text, ElementName=SearchTextBox}"/>
    </i:EventTrigger>
  <i:Interaction.Triggers>
</TextBox>

In the viewmodel

m_TestTextChangedCommand = new RelayCommand<string>(val => System.Diagnostics.Debug.WriteLine(val));

As you can see I used a commandparameter to pass the value of the textbox to the viewmodel. This way the viewmodel doesn't have to know about the textbox to get the text value.

An alternative to this approach would be to use behaviours and TwoWay binding to update a property:

<TextBox Name="SearchTextBox" Text="{Binding TextInViewModel, Mode=TwoWay}" >
  <i:Interaction.Behaviors>
    <sc:UpdateOnTextChangedBehavior/>
  </i:Interaction.Behaviors>
</TextBox>

UpdateOnTextChangedBehavior class:

    public class UpdateOnTextChangedBehavior : Behavior<TextBox>
    {
        protected override void OnAttached()
        {
            base.OnAttached();

            this.AssociatedObject.TextChanged += 
                new TextChangedEventHandler(AssociatedObject_TextChanged);
        }

        void AssociatedObject_TextChanged(object sender, TextChangedEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine(((TextBox)sender).Text);
            BindingExpression binding = 
                this.AssociatedObject.GetBindingExpression(TextBox.TextProperty);
            if (binding != null)
            {
                binding.UpdateSource();
            }
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();

            this.AssociatedObject.TextChanged -= 
                new TextChangedEventHandler(AssociatedObject_TextChanged);
        }
    }

What the above does is mimick the behaviour of desktop WPF Binding with UpdateSourceTrigger=PropertyChanged, which is missing in Silverlight. So what will happen, whenever you type into the text box TextInViewModel property will get updated. This property doesn't haven to be a DependencyProperty, it could just be a normal CLR property.

like image 84
Igor Zevaka Avatar answered Mar 03 '23 20:03

Igor Zevaka