共用方式為


ObservableProperty 屬性

ObservableProperty 類型是一個屬性,允許從批注字段產生可觀察的屬性。 其目的是要大幅減少定義可觀察屬性所需的重複使用量。

注意

若要運作,批注字段必須位於具有必要基礎結構的部分INotifyPropertyChanged類別中。 如果類型是巢狀的,宣告語法樹狀結構中的所有類型也必須標註為部分。 這樣做會導致編譯錯誤,因為產生器將無法產生具有所要求可觀察屬性之該型別的不同部分宣告。

平臺 API:、、、NotifyPropertyChangedRecipientsNotifyDataErrorInfoIRelayCommandNotifyCanExecuteChangedForNotifyPropertyChangedForICommandObservableValidator、 PropertyChangedMessage<T>ObservablePropertyIMessenger

運作方式

ObservableProperty屬性可用來標註部分類型中的欄位,如下所示:

[ObservableProperty]
private string? name;

其會產生如下的可觀察屬性:

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

它也會使用優化的實作來執行此動作,因此最終結果會更快。

注意

產生的屬性名稱將會根據功能變數名稱來建立。 產生器假設欄位命名 lowerCamel為、 _lowerCamelm_lowerCamel,而且它會轉換為 UpperCamel 遵循適當的 .NET 命名慣例。 產生的屬性一律會有公用存取子,但字段可以使用任何可見度來宣告(private 建議使用)。

變更時執行程序代碼

產生的程式代碼實際上比這個複雜一點,原因是它也公開了一些方法,以連結至通知邏輯,並在屬性即將更新時執行其他邏輯,並視需要在更新之後執行其他邏輯。 也就是說,產生的程式代碼實際上類似這樣:

public string? Name
{
    get => name;
    set
    {
        if (!EqualityComparer<string?>.Default.Equals(name, value))
        {
            string? oldValue = name;
            OnNameChanging(value);
            OnNameChanging(oldValue, value);
            OnPropertyChanging();
            name = value;
            OnNameChanged(value);
            OnNameChanged(oldValue, value);
            OnPropertyChanged();
        }
    }
}

partial void OnNameChanging(string? value);
partial void OnNameChanged(string? value);

partial void OnNameChanging(string? oldValue, string? newValue);
partial void OnNameChanged(string? oldValue, string? newValue);

這可讓您實作上述任何方法,以插入額外的程序代碼。 每當您想要執行一些只需要參考屬性已設定的新值時,前兩個就很有用。 每當您有一些更複雜的邏輯時,其他兩者也會很有用,這些邏輯也必須在設定的舊值和新值上更新一些狀態。

例如,以下是如何使用前兩個多載的範例:

[ObservableProperty]
private string? name;

partial void OnNameChanging(string? value)
{
    Console.WriteLine($"Name is about to change to {value}");
}

partial void OnNameChanged(string? value)
{
    Console.WriteLine($"Name has changed to {value}");
}

以下是如何使用其他兩個多載的範例:

[ObservableProperty]
private ChildViewModel? selectedItem;

partial void OnSelectedItemChanging(ChildViewModel? oldValue, ChildViewModel? newValue)
{
    if (oldValue is not null)
    {
        oldValue.IsSelected = true;
    }

    if (newValue is not null)
    {
        newValue.IsSelected = true;
    }
}

您可以自由地只在可用的方法之間實作任意數目的方法,或其中沒有任何方法。 如果未實作它們(或只有一個是),則編譯程式只會移除整個呼叫,因此在不需要這項額外功能的情況下,完全不會達到效能。

注意

產生的方法是沒有實作的部分方法,這表示如果您選擇實作它們,就無法為其指定明確的輔助功能。 也就是說,這些方法的實作也應該宣告為 just partial 方法,而且它們一律會隱含地具有私人輔助功能。 嘗試新增明確的輔助功能(例如新增 publicprivate),會導致錯誤,因為 C# 中不允許。

通知相依屬性

假設您有 FullName 想要在變更 Name 時引發通知的屬性。 您可以使用 屬性來執行此 NotifyPropertyChangedFor 動作,如下所示:

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
private string? name;

這會導致產生的屬性相當於下列專案:

public string? Name
{
    get => name;
    set
    {
        if (SetProperty(ref name, value))
        {
            OnPropertyChanged("FullName");
        }
    }
}

通知相依命令

假設您有一個命令,其執行狀態相依於此屬性的值。 也就是說,每當屬性變更時,命令的執行狀態應該會失效並再次計算。 換句話說, ICommand.CanExecuteChanged 應該再次引發。 您可以使用 屬性來達成此目的 NotifyCanExecuteChangedFor

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(MyCommand))]
private string? name;

這會導致產生的屬性相當於下列專案:

public string? Name
{
    get => name;
    set
    {
        if (SetProperty(ref name, value))
        {
            MyCommand.NotifyCanExecuteChanged();
        }
    }
}

為了讓此作業能夠運作,目標命令必須是一些 IRelayCommand 屬性。

要求屬性驗證

如果在繼承自 ObservableValidator 的型別中宣告屬性,也可以使用任何驗證屬性來標註該屬性,然後要求產生的 setter 觸發該屬性的驗證。 這可以使用 屬性來達成 NotifyDataErrorInfo

[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[MinLength(2)] // Any other validation attributes too...
private string? name;

這會導致產生下列屬性:

public string? Name
{
    get => name;
    set
    {
        if (SetProperty(ref name, value))
        {
            ValidateProperty(value, "Value2");
        }
    }
}

ValidateProperty產生的呼叫接著會驗證 屬性並更新物件的狀態ObservableValidator,讓UI元件可以回應它,並適當地顯示任何驗證錯誤。

注意

根據設計,只有繼承自 ValidationAttribute 的欄位屬性會轉送至產生的屬性。 這是為了支持數據驗證案例而特別完成。 將會忽略所有其他欄位屬性,因此目前無法在欄位上新增其他自定義屬性,並將它們也套用至產生的屬性。 如果需要的話(例如控制串行化),請考慮改用傳統的手動屬性。

傳送通知訊息

如果在繼承自 ObservableRecipient的類型中宣告 屬性,您可以使用 NotifyPropertyChangedRecipients 屬性指示產生器也插入程式碼,以傳送屬性變更的屬性變更訊息。 這可讓已註冊的收件者動態響應變更。 也就是說,請考慮下列程序代碼:

[ObservableProperty]
[NotifyPropertyChangedRecipients]
private string? name;

這會導致產生下列屬性:

public string? Name
{
    get => name;
    set
    {
        string? oldValue = name;

        if (SetProperty(ref name, value))
        {
            Broadcast(oldValue, value);
        }
    }
}

接著,產生的Broadcast呼叫將會使用IMessenger目前 viewmodel 中使用的 實例,將新的 PropertyChangedMessage<T> 傳送給所有已註冊的訂閱者。

新增自訂屬性

在某些情況下,在產生的屬性上也有一些自定義屬性可能很有用。 若要達成此目的,您只要在屬性清單中透過批注字段使用 [property: ] 目標,MVVM 工具組就會自動將這些屬性轉送至產生的屬性。

例如,請考慮如下的欄位:

[ObservableProperty]
[property: JsonRequired]
[property: JsonPropertyName("name")]
private string? username;

這會產生屬性,其中包含這兩[JsonRequired]Username 屬性和[JsonPropertyName("name")]屬性。 您可以使用視需要以屬性為目標的多個屬性清單,而且所有屬性都會轉送至產生的屬性。

範例

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