MVVM Light太快了:)

时间:2010-05-02 09:37:51

标签: silverlight windows-phone-7 mvvm command mvvm-light

我有一个简单的WM7页面TextBox。此外,我已为此EventToCommand分配RelayCommand<string>TextBox},对TextChanged事件作出反应。为了测试pourposes,我在页面的代码后面添加了另外的方法TextBox_TextChanged。命令和TextBox_TextChanged都打印带有文本框内容的消息框。

TextBox的初始值为"ABC"。然后我按下D和:

  1. TextBox_TextChanged打印ABCD
  2. 该命令打印ABC。 D缺失了。
  3. 为什么命令这么快?

    命令声明:

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

    命令初始化:

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

    命令绑定:

    <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>
    

4 个答案:

答案 0 :(得分:6)

我无法重现此行为。我尝试过使用EventToCommand和一个Behavior(它只是监听TextChanged事件)。

在没有看到代码的情况下,我怀疑这可能与您如何获取搜索框的文本或其他地方的逻辑错误有关。

这是我如何使用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>

在viewmodel中

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

如您所见,我使用命令参数将文本框的值传递给viewmodel。这样,viewmodel不必知道文本框以获取文本值。

此方法的替代方法是使用行为和TwoWay绑定来更新属性:

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

UpdateOnTextChangedBehavior类:

    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);
        }
    }

以上操作模仿桌面WPF BindingUpdateSourceTrigger=PropertyChanged的行为,而Silverlight中缺少该行为。那么无论何时键入文本框TextInViewModel属性都会发生更新。此属性不是DependencyProperty,它可能只是一个普通的CLR属性。

答案 1 :(得分:1)

这适用于RelayCommand的TextBox参数。我 - RelayCommand<TextBox>

    <TextBox Height="72" HorizontalAlignment="Left" Margin="8,136,0,0" Name="txtFilter" Text="" VerticalAlignment="Top" Width="460" >
        <interactivity:Interaction.Triggers>
            <interactivity:EventTrigger EventName="TextChanged">
                <cmd:EventToCommand Command="{Binding SearchedTextChanged}" CommandParameter="{Binding ElementName=txtFilter}" />
            </interactivity:EventTrigger>
        </interactivity:Interaction.Triggers>
    </TextBox>

public RelayCommand<TextBox> SearchedTextChanged { get; set; }

SearchedTextChanged = new RelayCommand<TextBox>(OnSearchedTextChanged);

private void OnSearchedTextChanged(TextBox val)
    {
        if (val != null)
        {
            System.Diagnostics.Debug.WriteLine(val.Text);
        }
    }

答案 2 :(得分:1)

我有一个类似的问题,发现数据绑定操作并不总是触发,直到TextBox失去焦点。但是,命令会立即触发。

如果要在使用该值之前保证已进行数据绑定,可以在控件上调用BindingExpression.UpdateSource()方法。尝试这样的事情:

var bindTarget = SearchTextBox.GetBindingExpression(TextBox.TextProperty);
bindTarget.UpdateSource();

为避免在ViewModel中直接引用TextBox(与MVVM一样),可以使用FocusManager.GetFocusedElement()。这在处理ApplicationBar按钮时特别有用,因为它们在使用时似乎不会获得焦点。

答案 3 :(得分:0)

我起诉的一些代码(类似于你的命令示例):

命令声明:

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

命令初始化:

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

命令绑定:

<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>

由于某些原因,messagebox会显示一个字符延迟的字符串。