具有对PropertyChanged的多个调用的ViewModel属性

本文关键字:调用 ViewModel 属性 PropertyChanged | 更新日期: 2023-09-27 18:25:28

最近我一直在学习C#和WPF。我正试图在我正在进行的一个项目中使用MVVM,只是为了保持代码的有序性并了解它是如何工作的。

在MVVM中,视图上的控件绑定到ViewModel上的属性,ViewModel实现INotifyPropertyChanged。通常,当某个属性更新时,我会希望其他一些属性也随之更新。

例如,我有一个ListBox,上面有一个TextBox。你可以在TextBox中键入,它会过滤ListBox中的内容。但在某些情况下,我还需要能够从代码中清除TextBox。代码最终看起来是这样的:

private Collection<string> _listOfStuff;
public Collection<string> FilteredList
{
    get
    {
        if (String.IsNullOrWhiteSpace(SearchText))
        {
            return _listOfStuff;
        }
        else
        {
            return new Collection<string>(_listOfStuff.Where(x => x.Contains(SearchText)));
        }
    }
    set
    {
        if (value != _listOfStuff)
        {
            _listOfStuff = value;
            OnPropertyChanged("FilteredList");
        }
    }
}
private string _searchText;
public string SearchText
{
    get { return _searchText; }
    set
    {
        if (value != _searchText)
        {
            _searchText = value;
            OnPropertyChanged("SearchText"); // Tells the view to change the value of the TextBox
            OnPropertyChanged("FilteredList"); // Tells the view to update the filtered list
        }
    }
}

随着这个项目越来越大,这开始让人觉得草率。我有一个setter,有6个对OnPropertyChanged的调用,很难跟踪这些东西。有更好的方法吗?

具有对PropertyChanged的多个调用的ViewModel属性

大约一年前,我在一个项目中试用了Assisticant。它会计算出您的哪些属性需要发出通知,以及哪些是相关的。Pluralsight上有一个很好的课程,网站上的例子也很好。如果没有其他东西,你可以查看源代码,看看他是如何做到的。

还有一些来自MVVM层次结构中的更改通知的好建议。

他们提到:
使用属性->例如[DependensUpon(name of(Size))]

Josh Smith的PropertyObserver

如果每次只需要引发相同的通知,可以将引发属性更改调用放在方法中。

首先,您不应该在命令中执行可能代价高昂的操作,然后您可以从SearchText中删除OnPropertyChanged("FilteredList");

因此,您应该将代码从getter移到它自己的命令中,并从XAML绑定它(作为按钮上的命令,或者在文本字段值更改时使用Blends Interactivity Trigger调用它)。

public ICommand SearchCommand { get; protected set; }
// Constructor
public MyViewModel()
{
    // DelegateCommand.FromAsyncHandler is from Prism Framework, but you can use
    // whatever your MVVM framework offers for async commands
    SearchCommand = DelegateCommand.FromAsyncHandler(DoSearch);
}
public async Task DoSearch() 
{
    var result = await _listOfStuff.Where(x => x.Contains(SearchText)).ToListAsync();
    FilteredList = new Collection<string>(result);
}
private Collection<string> _listOfStuff;
private Collection<string> _filteredList;
public Collection<string> FilteredList
{
    get
    {
        return _filteredList;
    }
    set
    {
        if (value != _filteredList)
        {
            _filteredList = value;
            OnPropertyChanged("FilteredList");
        }
    }
}
private string _searchText;
public string SearchText
{
    get 
    { 
        return _searchText;
    }
    set
    {
        if (value != _searchText)
        {
            _searchText = value;
            OnPropertyChanged("SearchText");
        }
    }
}

附带说明:您也可以使用OnPropertyChanged(nameof(FilteredList));来获得一个易于重构的版本,当您重命名属性时,所有OnPropertyChanged调用都将更新为。虽然需要C#6.0,但它与旧的.NET Framework(回到2.0)兼容,但需要Visual Studio 2015或更高版本的

对于任何正在寻找这类问题的好解决方案的人:请查看ReactiveUI。

它是一个基于反应式扩展(Rx)的框架,其理念是显式地对属性之间的这种依赖关系进行建模,而不需要RaisePropertyChanged(..)

具体查看ObservableAsPropertyHelper(有时称为OAPH)。

您应该只在属性本身的setter中引发OnPropertyChanged

ViewModel的一个更干净的实现可以是:

private Collection<string> _listOfStuff;
private Collection<string> _filteredList;
public Collection<string> FilteredList
{
    get
    {         
            return _filteredList;
    }
    set
    {
        if (value != _filteredList)
        {
            _filteredList = value;
            OnPropertyChanged("FilteredList");
        }
    }
}
private string _searchText;
public string SearchText
{
    get { return _searchText; }
    set
    {
        if (value != _searchText)
        {
            _searchText = value;
            OnPropertyChanged("SearchText");
            FilteredList = new Collection<string>(_listOfStuff.Where(x => x.Contains(SearchText)));
        }
    }
}

如果你只是不想键入,只有其他选项是为所有属性触发OnPropertyChanged,这可以通过传递null或字符串来完成。空,尽管它将是更糟糕的代码!

OnPropertyChanged(Null);

OnPropertyChanged(String.Empty);