ViewModel属性,多次调用PropertyChanged

时间:2015-12-03 17:13:02

标签: c# wpf mvvm

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

在MVVM中,View上的控件绑定到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
        }
    }
}

随着这个项目变得越来越大,这开始变得邋..我有一个安装者,有6个电话OnPropertyChanged,而且很难跟踪内容。有更好的方法吗?

5 个答案:

答案 0 :(得分:1)

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

因此,您应该将该代码从getter移动到它自己的命令中并将其绑定到XAML(作为按钮上的Command或使用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 Frameworks兼容(回到2.0),但需要Visual Studio 2015或更高版本

答案 1 :(得分:1)

一年前我在一个项目上尝试过Assisticant。它确定了哪些属性需要提出通知以及哪些属性相关。 Pluralsight上有一个很好的课程,网站上的例子非常好。如果没有别的,你可以查看源代码,看看他是如何做到的。

来自Change Notification in MVVM Hierarchies的一些好建议。

他们提到:
使用属性 - &gt;例如[DependsUpon(nameof(尺寸))]

Josh Smith的PropertyObserver

如果您只是需要每次都提出相同的通知,可以将raise属性更改调用放在方法中。

答案 2 :(得分:1)

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

这是一个基于Reactive Extensions(Rx)的框架,其理念是您可以明确地在属性之间建模这种类型的依赖关系,而不需要RaisePropertyChanged(..)的丛林。

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

答案 3 :(得分:0)

你应该只在房产本身的设定者中提出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)));
        }
    }
}

答案 4 :(得分:-2)

如果你只是不想键入其他选项就是为所有属性触发OnPropertyChanged,这可以通过传递null或string.Empty来完成,虽然它会是更粗略的代码!

kubectl exec

OnPropertyChanged(Null);