共用方式為


ObservableObject

ObservableObject是實作和 INotifyPropertyChanging 介面可INotifyPropertyChanged觀察之物件的基類。 它可以做為支援屬性變更通知之各種物件的起點。

平臺 API:ObservableObject、、 TaskNotifierTaskNotifier<T>

運作方式

ObservableObject 具有下列主要功能:

  • 它提供 和INotifyPropertyChanging的基底實作INotifyPropertyChanged,公開 PropertyChangedPropertyChanging 事件。
  • 它提供一系列 SetProperty 方法,可用來輕鬆地設定繼承自 ObservableObject的類型屬性值,並自動引發適當的事件。
  • 它提供 SetPropertyAndNotifyOnCompletion 方法,這類似於 SetProperty ,但能夠設定 Task 屬性,並在指派的工作完成時自動引發通知事件。
  • 它會公開 OnPropertyChangedOnPropertyChanging 方法,這些方法可以在衍生型別中覆寫,以自定義如何引發通知事件。

Simple 屬性

以下範例說明如何實作自定義屬性的通知支援:

public class User : ObservableObject
{
    private string name;

    public string Name
    {
        get => name;
        set => SetProperty(ref name, value);
    }
}

提供的 SetProperty<T>(ref T, T, string) 方法會檢查屬性的目前值,並在不同時加以更新,然後也會自動引發相關事件。 屬性名稱會透過屬性的使用 [CallerMemberName] 自動擷取,因此不需要手動指定要更新的屬性。

包裝不可觀察的模型

例如,使用資料庫專案時,常見的案例是建立包裝的「可系結」模型,以轉接資料庫模型的屬性,並在需要時引發屬性變更通知。 當想要將通知支援插入至未實作 介面的 INotifyPropertyChanged 模型時,也需要此專案。 ObservableObject 提供專用方法,讓此程式更簡單。 在下列範例中, User 是直接對應資料庫資料表的模型,而不繼承自 ObservableObject

public class ObservableUser : ObservableObject
{
    private readonly User user;

    public ObservableUser(User user) => this.user = user;

    public string Name
    {
        get => user.Name;
        set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
    }
}

在此情況下,我們使用 多 SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string) 載。 簽章比上一個簽章稍微複雜一點,即使我們無法存取上一個案例中的備份欄位,這也是讓程式碼仍然非常有效率的必要條件。 我們可以詳細查看此方法簽章的每個部分,以瞭解不同元件的角色:

  • TModel 是類型自變數,表示我們要包裝的模型類型。 在此情況下,這將是我們的 User 類別。 請注意,我們不需要明確指定此專案 - C# 編譯程式會藉由叫 SetProperty 用 方法的方式自動推斷此專案。
  • T 是我們想要設定的屬性類型。 類似於 TModel,這會自動推斷。
  • T oldValue 是第一個參數,在此案例中,我們會使用 user.Name 傳遞我們包裝之屬性的目前值。
  • T newValue 是要設定為 屬性的新值,而在這裡我們會傳遞 value,這是屬性 setter 內的輸入值。
  • TModel model 是我們包裝的目標模型,在此案例中,我們會傳遞儲存在欄位中的 user 實例。
  • Action<TModel, T> callback 是一個函式,如果屬性的新值與目前值不同,而且必須設定 屬性,就會叫用此函式。 這會由這個回呼函式來完成,此函式會接收作為目標模型的輸入,以及要設定的新屬性值。 在此情況下,我們只是將輸入值(我們呼叫 n的)指派給 Name 屬性(藉由執行 u.Name = n)。 請務必避免從目前範圍擷取值,並且只與指定做為回呼輸入的值互動,因為這可讓 C# 編譯程式快取回呼函式並執行一些效能改善。 這是因為我們不只是直接存取 user setter 中的欄位或 value setter 中的 參數,而是只使用 Lambda 表達式的輸入參數。

方法 SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string) 會讓建立這些包裝屬性非常簡單,因為它會同時處理擷取和設定目標屬性,同時提供非常精簡的 API。

注意

相較於使用 LINQ 表達式的這個方法實作,特別是透過型 Expression<Func<T>> 別的參數,而不是狀態和回呼參數,可達成這種方式的效能改善確實相當重要。 特別是,此版本比使用 LINQ 運算式快約 200 倍,而且完全不會進行任何記憶體配置。

處理 Task<T> 屬性

如果屬性是 Task ,也必須在工作完成時引發通知事件,以便在正確的時間更新系結。例如,若要在工作所代表的作業上顯示載入指標或其他狀態資訊。 ObservableObject 具有此案例的 API:

public class MyModel : ObservableObject
{
    private TaskNotifier<int>? requestTask;

    public Task<int>? RequestTask
    {
        get => requestTask;
        set => SetPropertyAndNotifyOnCompletion(ref requestTask, value);
    }

    public void RequestValue()
    {
        RequestTask = WebService.LoadMyValueAsync();
    }
}

在這裡, SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>, Task<T>, string) 方法會負責更新目標欄位、監視新工作、如果有,並在該工作完成時引發通知事件。 如此一來,就可以直接系結至工作屬性,並在其狀態變更時收到通知。 TaskNotifier<T>是所ObservableObject公開的特殊型別,會包裝目標Task<T>實例,並啟用這個方法的必要通知邏輯。 TaskNotifier如果您只有一般Task類型,也可以直接使用類型。

注意

方法SetPropertyAndNotifyOnCompletion的目的是要取代封裝中Microsoft.Toolkit型別的使用NotifyTaskCompletion<T>方式。 如果使用這個類型,它只能取代為 inner Task (或 Task<TResult>) 屬性,然後使用 SetPropertyAndNotifyOnCompletion 方法來設定其值並引發通知變更。 類型公開 NotifyTaskCompletion<T> 的所有屬性都可直接在 實例上使用 Task

範例

  • 查看 範例應用程式 (適用於多個 UI 架構),以查看 MVVM 工具組的運作情形。
  • 您也可以在單元測試中找到更多範例。