具有对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
的调用,很难跟踪这些东西。有更好的方法吗?
大约一年前,我在一个项目中试用了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);