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
,NotifyDataErrorInfo
NotifyCanExecuteChangedFor
,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 ObservableRecipient
klasy , 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.