【问题标题】:XAML, MVVM and TwoWay Data BindingXAML、MVVM 和双向数据绑定
【发布时间】:2018-07-27 17:53:33
【问题描述】:

我有以下 XAML。我正在使用 UWP(通用 Windows 平台),但我想描述的问题也适用于其他 XAML 框架,例如 WPF:

<!-- MyVM is a ViewModel -->
<AutoSuggestBox
    QueryIcon="Find"
    TextChanged="{x:Bind MyVM.FilterTextChanged}" 
    Text="{x:Bind MyVM.FilterText, Mode=TwoWay}"/>

它是这样工作的:当用户在AutoSuggestBox 中键入文本时,MyVM ViewModel 会被告知每个击键并使用FilterText 过滤数据。

这就是MyVM 的样子:

// Uses INotifyPropertyChanged from MVVMLight
private string filterText;
public string FilterText
{
    get { return filterText; }
    set { Set(ref filterText, value); }
}

public async void FilterTextChanged()
{
    await LoadData(); // uses FilterText to filter data
}

当我需要修改FilterText 值时出现问题,例如清除它或设置预定义的过滤器。由于 TwoWay 绑定,AutoSuggestBox 中的文本可以正确显示,但作为“副作用”,会调用 FilterTextChanged 方法(因为文本已更改)。我不想要这种“副作用”。不好有两个原因:

  1. 它使 ViewModel 依赖于 View 中的 XAML。我的意思是,虽然我不调用FilterTextChanged,但当我设置FilterText 值时,它还是会被调用,因为它在XAML 中是双向绑定的。

  2. 它使自动化单元测试成为不可能。如果没有 XAML,ViewModel 的行为会有所不同:当我设置 FilterText 值时,不会调用 FilterTextChanged 方法。

这是 XAML、MVVM 和 TwoWay 绑定的一般问题,不限于 AutoSuggestBox 的具体示例。

如何解决这个问题?我的主要问题是如何对其进行单元测试?

【问题讨论】:

    标签: wpf unit-testing xaml mvvm uwp-xaml


    【解决方案1】:

    当我需要修改 FilterText 值时出现问题,例如清除它或设置预定义的过滤器。由于 TwoWay 绑定,AutoSuggestBox 中的文本可以正确显示,但作为“副作用”,调用了 FilterTextChanged 方法(因为文本已更改)。我不想要这种“副作用”。

    如果我理解您的问题,您只是不希望在清空 AutoSuggestBox 或为其设置默认值时调用 FilterTextChanged。如果是这样,你就不需要使用TextChanged来触发FilterTextChanged方法,你可以在属性值改变时调用它。

    例如:

    <AutoSuggestBox QueryIcon="Find" Text="{x:Bind MyVM.FilterText, Mode=TwoWay}"/>
    
    private string filterText;
    public string FilterText
    {
        get { return filterText; }
        set
        {
            if (value != string.Empty || value != "default value")
            {
                FilterTextChanged();
            }
            filterText = value;
            RaisePropertyChanged("FilterText");
        }
    }
    
    public async void FilterTextChanged()
    {
        await LoadData(); // uses FilterText to filter data
    }
    

    【讨论】:

    • 然后,当用户从文本框中清除文本时,不会调用 FilterTextChanged 并且结果将保留在上一次搜索的 UI 上。我建议在“值!= filterText”(上一个值)时从设置器中调用 FilterTextChanged 方法。每当需要恢复为默认值时,将私有变量“filterText”设置为默认值或空字符串,并引发为绑定更改的属性。
    • 感谢@Xavier 和@ParameterX。看起来关键是在FilterText 的setter 中调用RaisePropertyChanged,而不是依赖TwoWay 绑定。不过,您的方法存在一个小问题:FilterTextChange 方法是 async,但在 FilterText 的设置器中,它没有被等待。我知道 C# 属性不能是async。我应该保持原样吗?不在这里等待async 方法的后果是什么? (目前,我可以在 VS 中看到警告)
    • setter 中调用的异步方法的任何解决方案?我认为这不是我想的那么小问题。如果一个方法是异步的,它应该被等待。它使建议的解决方案不适用。
    • 你使用了async void方法,当你调用FilterTextChanged方法时你不必使用await,因为你在它的方法中使用了await
    • FilterTextChanged 方法是async void 而不是async Task 的原因是在我的原始代码中它在XAML 中绑定到TextChanged。从代码调用时,不应该返回async Task并等待吗?
    猜你喜欢
    • 2021-01-27
    • 1970-01-01
    • 1970-01-01
    • 2016-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-04
    • 2016-09-02
    相关资源
    最近更新 更多