Udostępnij za pośrednictwem


Atrybut ObservableProperty

Typ ObservableProperty jest atrybutem, który umożliwia generowanie obserwowalnych właściwości z pól z adnotacjami. Jego celem jest znaczne zmniejszenie ilości kotła potrzebnego do zdefiniowania obserwowalnych właściwości.

Uwaga

Aby można było pracować, pola z adnotacjami muszą znajdować się w klasie częściowej z niezbędną INotifyPropertyChanged infrastrukturą. Jeśli typ jest zagnieżdżony, wszystkie typy w drzewie składni deklaracji muszą być również oznaczone jako częściowe. Nie spowoduje to wystąpienia błędów kompilacji, ponieważ generator nie będzie mógł wygenerować innej częściowej deklaracji tego typu z żądaną obserwowalnymi właściwościami.

Interfejsy API platformy:ObservableProperty, NotifyPropertyChangedFor, NotifyDataErrorInfoNotifyCanExecuteChangedFor, NotifyPropertyChangedRecipients, ICommand, IRelayCommand, ObservableValidator, PropertyChangedMessage<T>,IMessenger

Jak to działa

Atrybut ObservableProperty może służyć do dodawania adnotacji do pola w typie częściowym, w następujący sposób:

[ObservableProperty]
private string? name;

Spowoduje to wygenerowanie obserwowalnej właściwości w następujący sposób:

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

Zrobi to również przy użyciu zoptymalizowanej implementacji, więc wynik końcowy będzie jeszcze szybszy.

Uwaga

Nazwa wygenerowanej właściwości zostanie utworzona na podstawie nazwy pola. Generator zakłada, że pole ma nazwę lowerCamel, _lowerCamel lub m_lowerCamel, i przekształci je tak, aby UpperCamel było zgodne z odpowiednimi konwencjami nazewnictwa platformy .NET. Wynikowa właściwość zawsze będzie mieć publiczne metody dostępu, ale pole można zadeklarować z dowolną widocznością (private jest zalecane).

Uruchamianie kodu po zmianach

Wygenerowany kod jest w rzeczywistości nieco bardziej złożony, a przyczyną jest to, że uwidacznia również niektóre metody, które można zaimplementować, aby podłączyć logikę do logiki powiadomień, i uruchomić dodatkową logikę, gdy właściwość ma zostać zaktualizowana i bezpośrednio po jej zaktualizowaniu, w razie potrzeby. Oznacza to, że wygenerowany kod jest w rzeczywistości podobny do następującego:

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

Dzięki temu można zaimplementować dowolną z tych metod, aby wstrzyknąć dodatkowy kod. Pierwsze dwa są przydatne za każdym razem, gdy chcesz uruchomić logikę, która musi odwoływać się tylko do nowej wartości, do której ustawiono właściwość. Pozostałe dwa są przydatne zawsze, gdy masz bardziej złożoną logikę, która również musi zaktualizować jakiś stan zarówno dla starej, jak i nowej wartości.

Oto przykład użycia dwóch pierwszych przeciążeń:

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

Oto przykład użycia dwóch pozostałych przeciążeń:

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

Możesz zaimplementować tylko dowolną liczbę metod spośród tych, które są dostępne, lub żadna z nich. Jeśli nie zostaną one zaimplementowane (lub jeśli tylko jeden z nich), całe wywołania zostaną usunięte tylko przez kompilator, więc nie będzie żadnych trafień wydajności w ogóle w przypadkach, gdy ta dodatkowa funkcja nie jest wymagana.

Uwaga

Wygenerowane metody to metody częściowe bez implementacji, co oznacza, że jeśli zdecydujesz się je zaimplementować, nie można określić jawnego ułatwień dostępu. Oznacza to, że implementacje tych metod powinny być również deklarowane jako tylko partial metody i zawsze będą miały niejawny dostęp prywatny. Próba dodania jawnego ułatwień dostępu (np. dodawanie public lub private) spowoduje wystąpienie błędu, ponieważ nie jest to dozwolone w języku C#.

Powiadamianie właściwości zależnych

Wyobraź sobie, FullName że masz właściwość, dla której chcesz zgłosić powiadomienie za każdym razem, gdy Name zmienisz się. Możesz to zrobić, używając atrybutu NotifyPropertyChangedFor , w następujący sposób:

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

Spowoduje to wygenerowanie właściwości równoważnej temu:

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

Powiadamianie poleceń zależnych

Wyobraź sobie, że masz polecenie, którego stan wykonywania był zależny od wartości tej właściwości. Oznacza to, że za każdym razem, gdy właściwość uległa zmianie, stan wykonywania polecenia powinien zostać unieważniony i obliczony ponownie. Innymi słowy, ICommand.CanExecuteChanged należy podnieść ponownie. Można to osiągnąć za pomocą atrybutu NotifyCanExecuteChangedFor :

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

Spowoduje to wygenerowanie właściwości równoważnej temu:

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

Aby to zadziałało, polecenie docelowe musi być jakąś IRelayCommand właściwością.

Żądanie weryfikacji właściwości

Jeśli właściwość jest zadeklarowana w typie, który dziedziczy z ObservableValidator, można również dodać do niej adnotacje z dowolnymi atrybutami walidacji, a następnie zażądać wygenerowanego elementu setter w celu wyzwolenia walidacji dla tej właściwości. Można to osiągnąć za pomocą atrybutu NotifyDataErrorInfo :

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

Spowoduje to wygenerowanie następującej właściwości:

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

To wygenerowane ValidateProperty wywołanie następnie zweryfikuje właściwość i zaktualizuje stan ObservableValidator obiektu, aby składniki interfejsu użytkownika mogły reagować na nie i odpowiednio wyświetlać wszelkie błędy walidacji.

Uwaga

Zgodnie z projektem tylko atrybuty pól dziedziczone z ValidationAttribute zostaną przekazane do wygenerowanej właściwości. Jest to wykonywane specjalnie w celu obsługi scenariuszy weryfikacji danych. Wszystkie inne atrybuty pola zostaną zignorowane, więc nie jest obecnie możliwe dodanie dodatkowych atrybutów niestandardowych w polu i zastosowanie ich również do wygenerowanej właściwości. Jeśli jest to wymagane (np. do kontrolowania serializacji), rozważ użycie tradycyjnej właściwości ręcznej.

Wysyłanie komunikatów powiadomień

Jeśli właściwość jest zadeklarowana w typie, który dziedziczy z ObservableRecipientklasy , możesz użyć atrybutu NotifyPropertyChangedRecipients , aby poinstruować generator, aby również wstawić kod w celu wysłania komunikatu o zmianie właściwości dla zmiany właściwości. Umożliwi to zarejestrowanym adresatom dynamiczne reagowanie na zmianę. Oznacza to, że należy wziąć pod uwagę następujący kod:

[ObservableProperty]
[NotifyPropertyChangedRecipients]
private string? name;

Spowoduje to wygenerowanie następującej właściwości:

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

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

Broadcast Wygenerowane wywołanie spowoduje wysłanie nowego PropertyChangedMessage<T> wystąpienia używanego IMessenger w bieżącym modelu viewmodel do wszystkich zarejestrowanych subskrybentów.

Dodawanie atrybutów niestandardowych

W niektórych przypadkach przydatne może być również posiadanie niektórych atrybutów niestandardowych dla wygenerowanych właściwości. Aby to osiągnąć, można po prostu użyć [property: ] elementu docelowego na listach atrybutów w polach z adnotacjami, a zestaw narzędzi MVVM automatycznie przekaże te atrybuty do wygenerowanych właściwości.

Rozważmy na przykład następujące pole:

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

Spowoduje to wygenerowanie Username właściwości z tymi dwoma [JsonRequired] atrybutami i [JsonPropertyName("name")] . Można użyć jak najwięcej list atrybutów przeznaczonych dla właściwości zgodnie z potrzebami, a wszystkie z nich zostaną przekazane do wygenerowanych właściwości.

Przykłady

  • Zapoznaj się z przykładową aplikacją (dla wielu struktur interfejsu użytkownika), aby zobaczyć, jak działa zestaw narzędzi MVVM Toolkit.
  • Więcej przykładów można również znaleźć w testach jednostkowych.