2

我正在使用 MvmmCross v3 框架和Windows.UI.Interactivity 开发WinRT 。

我想要一个带有 EventTrigger 的 TextBox,它在启动命令的事件 TextChanged 上。而且,我希望在 CommandParameter 中包含 textBox 的文本。

所以我有这个代码

   <i:EventTrigger EventName="TextChanged">
                    <i:InvokeCommandAction  Command="{Binding UpdateText}" CommandParameter="{Binding Text}"/>
                </i:EventTrigger>


public ICommand UpdateText
{
    get
    {
        return new MvxCommand<object>((textSearch) =>
            {
                // code...
            });
    }
}

但是我的 textSearch 参数等于“{Windows.UI.Xaml.Controls.TextChangedEventArgs}”,所有这些属性都为 NULL。

我还尝试在绑定中明确声明我的 ElementName,例如

CommandParameter="{Binding Path=Text, ElementName=tes}

但它也失败了。

谢谢

4

1 回答 1

7

你真的需要处理TextChanged事件吗?只需绑定到Text属性,您就可以收到更改通知:

<TextBox Text="{Binding TextValue, Mode=TwoWay}" />

然后在视图模型中:

private string _textValue;
public string TextValue
{
    get
    {
        return _textValue;
    }
    set
    {
        if (_textValue == value)
            return;
        _textValue = value;
        OnTextChanged(value); // react to the changed value
    }
}

编辑:

Text如果你想从你的内部获取价值,你需要注意两件事Command

首先,您需要修复CommandParameter绑定。通过使用{Binding Text},您实际上是在尝试绑定到视图模型中的属性,即您首先需要将该TextBox.Text属性绑定到相同的视图模型属性。正如您在评论中所说,这对您没有好处,因为您需要有关每次更改的信息,而不仅仅是失去焦点的信息,因此您获得的价值还不够最新。

因此,正确的方法是您的第二次尝试,即直接绑定到TextBoxusingElementName语法。不幸的是,触发器不是可视化树的一部分,因此它们无法访问 XAML 名称范围(您可以在此博客文章中了解更多信息)。您可以通过使用 MVVMHelpers.Metro 包来解决NameScopeBinding这个问题

<Behaviors:NameScopeBinding x:Key="MyTextBox" 
                            Source="{Binding ElementName=MyTextBox}" />

现在您可以使ElementName绑定工作:

<i:InvokeCommandAction Command="{Binding UpdateText}" 
    CommandParameter="{Binding Source.Text, Source={StaticResource MyTextBox}}"/>

你还有第二个问题。您绑定的Text值仅在失去焦点时更新,因此您在处理TextChanged事件时无法获得正确的值。解决方案是绑定到TextBox自身:

<i:InvokeCommandAction Command="{Binding UpdateText}" 
    CommandParameter="{Binding Source, Source={StaticResource MyTextBox}}"/>

然后在命令内部Text直接从以下获取属性TextBox

private void OnUpdateText(object parameter)
{
    var value = ((TextBox) parameter).Text;
    // process the Text value as you need to.
}

编辑2:

要使上述代码与 PCL 中的视图模型一起工作,您可以采取几种方法。

简单的技巧是使用反射。由于它在 PCL 中可用,因此您可以访问该Text属性并读取其值:

private void OnUpdateText(object parameter)
{
    var textProperty = textSearch.GetType().GetProperty("Text");
    var value = textProperty.GetValue(parameter, null) as string;
    // process the Text value as you need to.
}

更简洁的解决方案是将 WinRT 特定代码放入通过接口抽象的单独程序集中。您将首先在 PCL 库中创建一个接口:

public interface IUiService
{
    string GetTextBoxText(object textBox);
}

并在构造函数中修改视图模型以接受此接口:

public ViewModel(IUiService uiService)
{
    _uiService = uiService;
}

在命令处理程序中,您将使用界面中的方法:

private void OnUpdateText(object parameter)
{
    var value = _uiService.GetTextBoxText(parameter);
    // process the Text value as you need to.
}

您将在 WinRT 库中实现该接口:

public class UiService : IUiService
{
    public string GetTextBoxText(object textBox)
    {
        var typedTextBox = textBox as TextBox;
        return typedTextBox.text;
    }
}

在应用程序中,您引用此库并将实现传递给视图模型:

var viewModel = new ViewModel(new UiService);

最喜欢的方法是不同的:我会创建一个Behavior公开的Text属性,每次TextChanged触发事件时都会自动更新:

public class TextChangedBehavior : Behavior<TextBox>
{
    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        "Text", 
        typeof(string), 
        typeof(TextChangedBehavior), 
        new PropertyMetadata(null));

    public string Text
    {
        get { return (string) GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }


    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.TextChanged += OnTextChanged;
        Text = AssociatedObject.Text;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.TextChanged -= OnTextChanged;
    }

    private void OnTextChanged(object sender, TextChangedEventArgs textChangedEventArgs)
    {
        Text = AssociatedObject.Text;
    }
}

现在,我可以将一个TextValue属性绑定到此行为并对其在属性设置器中的更改做出反应,正如在这个长答案的开头所建议的那样:

<TextBox>
    <i:Interaction.Behaviors>
        <b:TextChangedBehavior Text="{Binding TextValue, Mode=TwoWay}" />
    </i:Interaction.Behaviors>
</TextBox>
于 2013-06-04T05:58:36.067 回答