C# 多次调用PropertyChanged的ViewModel属性

C# 多次调用PropertyChanged的ViewModel属性,c#,wpf,mvvm,C#,Wpf,Mvvm,最近我一直在学习C#和WPF的工作。我正试图在我正在从事的一个项目中使用MVVM,只是为了让代码有条理并了解它是如何工作的 在MVVM中,视图上的控件绑定到ViewModel上的属性,后者实现INotifyPropertyChanged。通常,当某个属性被更新时,我希望其他一些属性也随之更新 例如,我有一个列表框,上面有一个文本框。你可以输入文本框,它会过滤列表框中的内容。但在某些情况下,我还需要能够从代码中清除文本框。代码最终如下所示: private Collection<string

最近我一直在学习C#和WPF的工作。我正试图在我正在从事的一个项目中使用MVVM,只是为了让代码有条理并了解它是如何工作的

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

例如,我有一个列表框,上面有一个文本框。你可以输入文本框,它会过滤列表框中的内容。但在某些情况下,我还需要能够从代码中清除文本框。代码最终如下所示:

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
        }
    }
}
private Collection\u-listofsuff;
公共收集过滤器列表
{
得到
{
if(String.IsNullOrWhiteSpace(SearchText))
{
返回listOfStuff;
}
其他的
{
返回新集合(_listofsuff.Where(x=>x.Contains(SearchText));
}
}
设置
{
如果(值!=\u listOfStuff)
{
_listOfStuff=值;
OnPropertyChanged(“FilteredList”);
}
}
}
私有字符串_searchText;
公共字符串搜索文本
{
获取{return\u searchText;}
设置
{
如果(值!=\u搜索文本)
{
_搜索文本=值;
OnPropertyChanged(“SearchText”);//告诉视图更改文本框的值
OnPropertyChanged(“FilteredList”);//告诉视图更新筛选列表
}
}
}

随着这个项目越来越大,这开始让人觉得马虎。我有一个setter,有6个调用
OnPropertyChanged
,很难跟踪内容。有更好的方法吗?

如果您只是不想键入,那么唯一的选择是为所有属性触发PropertyChanged,这可以通过传递null或string.Empty来完成,尽管这将是更草率的代码

OnPropertyChanged(Null);


首先,您不应该在命令中执行可能代价高昂的操作,然后您就可以删除
OnPropertyChanged(“FilteredList”)从您的
搜索文本

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

public ICommand SearchCommand{get;protected set;}
//建造师
公共MyViewModel()
{
//DelegateCommand.FromAsyncHandler来自Prism框架,但您可以使用
//无论您的MVVM框架为异步命令提供什么
SearchCommand=DelegateCommand.FromAsyncHandler(DoSearch);
}
公共异步任务DoSearch()
{
var result=await_listOfStuff.Where(x=>x.Contains(SearchText)).toListSync();
FilteredList=新集合(结果);
}
私人藏品清单;
私人收藏过滤列表;
公共收集过滤器列表
{
得到
{
返回过滤器列表;
}
设置
{
if(value!=\u filteredList)
{
_filteredList=值;
OnPropertyChanged(“FilteredList”);
}
}
}
私有字符串_searchText;
公共字符串搜索文本
{
得到
{ 
返回搜索文本;
}
设置
{
如果(值!=\u搜索文本)
{
_搜索文本=值;
OnPropertyChanged(“搜索文本”);
}
}
}

在旁注上:您还可以在PropertyChanged(nameof(FilteredList))上使用
要获得一个重构友好的版本,当您重命名属性时,所有的
OnPropertyChanged
调用都将更新为。虽然需要C#6.0,但它与较旧的.NET框架兼容(回到2.0),但需要Visual Studio 2015或更高版本

您应该只在属性本身的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)));
        }
    }
}
private Collection\u-listofsuff;
私人收藏过滤列表;
公共收集过滤器列表
{
得到
{         
返回过滤器列表;
}
设置
{
如果(值!=\u过滤器列表)
{
_filteredList=值;
OnPropertyChanged(“FilteredList”);
}
}
}
私有字符串_searchText;
公共字符串搜索文本
{
获取{return\u searchText;}
设置
{
如果(值!=\u搜索文本)
{
_搜索文本=值;
OnPropertyChanged(“搜索文本”);
FilteredList=新集合(_listOfStuff.Where(x=>x.Contains(SearchText));
}
}
}

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

也有一些很好的建议

他们提到:
使用属性->例如[DependedSupon(名称(大小))]

Josh Smith的PropertyObserver


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

对于正在搜索此类问题的好解决方案的任何人,请签出

它是一个基于反应式扩展(Rx)的框架,其思想是在属性之间显式地建模这种类型的依赖关系,而不需要大量的
RaisePropertyChanged(…)


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

你说的草率是指大量的OnPropertyChanged语句?这正是你想要的,不是吗?你不必调用
OnPropertyChanged(“FilteredList”)此处,因为未更改/分配。当它被更改时(即在异步命令中),当您执行
FilteredList=wait someasynchmethod()
时,它将通知视图。你到底想归档什么@
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");
        }
    }
}
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)));
        }
    }
}