多次調用 PropertyChanged 的 ViewModel 屬性 (ViewModel properties with multiple calls to PropertyChanged)


問題描述

多次調用 PropertyChanged 的 ViewModel 屬性 (ViewModel properties with multiple calls to PropertyChanged)

最近我一直在學習 C# 和 WPF。我正在嘗試在我正在處理的項目中使用 MVVM,只是為了保持代碼井井有條並了解它是如何工作的。

在 MVVM 中,View 上的控件綁定到 ViewModel 上的屬性,這實現 INotifyPropertyChanged。很多時候,當某個屬性被更新時,我會想要一堆其他屬性也隨之更新。

例如,我有一個上面有一個 TextBox 的 ListBox。您可以在 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 的二傳手 並且越來越難以跟踪內容。有沒有更好的方法來做到這一點?


參考解法

方法 1:

First you shouldn't do potentially expensive operations in a command, then you'll be able to remove the OnPropertyChanged("FilteredList"); from your SearchText.

So you should move that code from the getter and into it's own command and bind it from XAML (either as Command on a button or using Blends Interactivity Trigger to call it when the text fields value changes).

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");
        }
    }
}

On a side note: You can also use OnPropertyChanged(nameof(FilteredList)); to have a refactor friendly version, when you rename your property all of your OnPropertyChanged calls will be updated to. Requires C# 6.0 though, but it's compatible with older .NET Frameworks (back to 2.0), but requires Visual Studio 2015 or later

方法 2:

I tried out Assisticant on a project about a year ago. It figures out which of your properties need to raise notifications and also which are related. There is a good course for it on Pluralsight and the examples on the website are pretty good. If nothing else you could check out the source code to see how he did it.

Also some good suggestions from Change Notification in MVVM Hierarchies.

They mentioned: Use an attribute ‑> e.g. [DependsUpon(nameof(Size))]

and

Josh Smith's PropertyObserver

Could put the raise property change calls in a method if you just need to raise the same notifications every time.

方法 3:

For anyone searching for a good solution to this type of problem: Check out ReactiveUI.

It is a framework based on Reactive Extensions (Rx), with the idea that you model this type of dependencies between properties explicitly, without a jungle of RaisePropertyChanged(..).

Specifically check out the ObservableAsPropertyHelper (sometimes called OAPH).

方法 4:

You should only raise OnPropertyChanged in the setter of the property itself.

A cleaner implementation of your ViewModel can be:

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)));
        }
    }
}

(by Patrick A.TsengJoseph EvensensamiremLucaV)

參考文件

  1. ViewModel properties with multiple calls to PropertyChanged (CC BY‑SA 2.5/3.0/4.0)

#mvvm #wpf #C#






相關問題

WPF View 在關閉時將 ViewModel 屬性設置為 null (WPF View sets ViewModel properties to null on closing)

在文本框中正確輸入後啟用按鈕 (Button Enable After Correct Input In TextBox)

WPF說數據項不為空時為空 (WPF says that data item is null when it is not null)

MVVM - 如何將 ViewModel 包裝在 ViewModel 中? (MVVM - How to wrap ViewModel in a ViewModel?)

關於服務參考和 MVVM 模式的幾個一般問題 (A few general questions about Service Reference and MVVM pattern)

WPF MVVM 鏈接視圖 (WPF MVVM Linked Views)

wpf 樹視圖 mvvm (wpf treeview mvvm)

如何在 MVVM Light 的 ListView 中的 ComboBox 中顯示列表? (How to show a List in a ComboBox in a ListView in MVVM Light?)

多次調用 PropertyChanged 的 ViewModel 屬性 (ViewModel properties with multiple calls to PropertyChanged)

如何將圖像存儲在類庫中並從任何類訪問它 (How can i store an image in a class library and access it from any class)

Silverlight MVVM 隔離存儲 (Silverlight MVVM Isolated Storage)

如何將文本框的borderBrush屬性綁定到viewmodel中的屬性,類型轉換錯誤 (How to bind the borderBrush property of a textbox to a property in viewmodel, type conversion error)







留言討論