Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Il ObservableProperty tipo è un attributo che consente di generare proprietà osservabili da campi annotati. Il suo scopo è ridurre notevolmente la quantità di boilerplate necessaria per definire le proprietà osservabili.
Nota
Per funzionare, i campi con annotazioni devono trovarsi in una classe parziale con l'infrastruttura necessaria INotifyPropertyChanged . Se il tipo è annidato, anche tutti i tipi nell'albero della sintassi della dichiarazione devono essere annotati come parziali. In caso contrario, si verifica un errore di compilazione, perché il generatore non sarà in grado di generare una dichiarazione parziale diversa di tale tipo con la proprietà osservabile richiesta.
API della piattaforma:
ObservableProperty,NotifyPropertyChangedFor,NotifyDataErrorInfoNotifyCanExecuteChangedFor,NotifyPropertyChangedRecipients, ,ICommand,IRelayCommandObservableValidator,PropertyChangedMessage<T>, ,IMessenger
Funzionamento
L'attributo ObservableProperty può essere usato per annotare un campo in un tipo parziale, ad esempio:
[ObservableProperty]
private string? name;
E genererà una proprietà osservabile simile alla seguente:
public string? Name
{
get => name;
set => SetProperty(ref name, value);
}
Lo farà anche con un'implementazione ottimizzata, quindi il risultato finale sarà ancora più veloce.
Nota
Il nome della proprietà generata verrà creato in base al nome del campo. Il generatore presuppone che il campo sia denominato lowerCamel, _lowerCamel o m_lowerCamele trasformerà tale campo in UpperCamel modo che segua le convenzioni di denominazione .NET appropriate. La proprietà risultante avrà sempre funzioni di accesso pubbliche, ma il campo può essere dichiarato con qualsiasi visibilità (private è consigliato).
Esecuzione del codice in caso di modifiche
Il codice generato è in realtà un po ' più complesso di questo, e il motivo è che espone anche alcuni metodi che è possibile implementare per eseguire l'hook nella logica di notifica ed eseguire logica aggiuntiva quando la proprietà sta per essere aggiornata e subito dopo l'aggiornamento, se necessario. Ovvero, il codice generato è in realtà simile al seguente:
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);
In questo modo è possibile implementare uno di questi metodi per inserire codice aggiuntivo. I primi due sono utili ogni volta che si vuole eseguire una logica che deve fare riferimento solo al nuovo valore su cui è stata impostata la proprietà. Gli altri due sono utili ogni volta che si ha una logica più complessa che deve anche aggiornare uno stato sia sul vecchio che sul nuovo valore impostato.
Ad esempio, di seguito è riportato un esempio di come usare i primi due overload:
[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}");
}
Di seguito è riportato un esempio di come usare gli altri due overload:
[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;
}
}
È possibile implementare solo un numero qualsiasi di metodi tra quelli disponibili o nessuno di essi. Se non vengono implementati (o se ne è solo uno), l'intera chiamata verrà semplicemente rimossa dal compilatore, quindi non ci sarà alcun riscontro delle prestazioni per tutti i casi in cui questa funzionalità aggiuntiva non è necessaria.
Nota
I metodi generati sono metodi parziali senza implementazione, ovvero se si sceglie di implementarli, non è possibile specificare un'accessibilità esplicita per tali metodi. Ovvero, le implementazioni di questi metodi devono anche essere dichiarate come metodi semplici partial e avranno sempre accessibilità privata in modo implicito. Il tentativo di aggiungere un'accessibilità esplicita (ad esempio, l'aggiunta public o private) genererà un errore, perché non è consentito in C#.
Notifica delle proprietà dipendenti
Si supponga di avere una FullName proprietà per cui si vuole generare una notifica per ogni modifica Name . A tale scopo, è possibile usare l'attributo NotifyPropertyChangedFor , come illustrato di seguito:
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
private string? name;
In questo modo verrà generata una proprietà equivalente a questa:
public string? Name
{
get => name;
set
{
if (SetProperty(ref name, value))
{
OnPropertyChanged("FullName");
}
}
}
Notifica dei comandi dipendenti
Si supponga di avere un comando il cui stato di esecuzione dipende dal valore di questa proprietà. Ovvero, ogni volta che la proprietà viene modificata, lo stato di esecuzione del comando deve essere invalidato e calcolato di nuovo. In altre parole, ICommand.CanExecuteChanged dovrebbe essere nuovamente generato. È possibile ottenere questo risultato usando l'attributo NotifyCanExecuteChangedFor :
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(MyCommand))]
private string? name;
In questo modo verrà generata una proprietà equivalente a questa:
public string? Name
{
get => name;
set
{
if (SetProperty(ref name, value))
{
MyCommand.NotifyCanExecuteChanged();
}
}
}
Per poter funzionare, il comando di destinazione deve essere una IRelayCommand proprietà.
Richiesta di convalida delle proprietà
Se la proprietà viene dichiarata in un tipo che eredita da ObservableValidator, è anche possibile annotarla con gli attributi di convalida e quindi richiedere al setter generato di attivare la convalida per tale proprietà. Questa operazione può essere ottenuta con l'attributo NotifyDataErrorInfo :
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[MinLength(2)] // Any other validation attributes too...
private string? name;
Ciò comporterà la generazione della proprietà seguente:
public string? Name
{
get => name;
set
{
if (SetProperty(ref name, value))
{
ValidateProperty(value, "Value2");
}
}
}
La chiamata generata ValidateProperty convaliderà quindi la proprietà e aggiornerà lo stato dell'oggetto, in modo che i componenti dell'interfaccia ObservableValidator utente possano reagire e visualizzare eventuali errori di convalida in modo appropriato.
Nota
Per impostazione predefinita, solo gli attributi di campo che ereditano da ValidationAttribute verranno inoltrati alla proprietà generata. Questa operazione viene eseguita in modo specifico per supportare scenari di convalida dei dati. Tutti gli altri attributi di campo verranno ignorati, quindi non è attualmente possibile aggiungere altri attributi personalizzati in un campo e applicarli anche alla proprietà generata. Se è necessario (ad esempio, per controllare la serializzazione), prendere in considerazione l'uso di una proprietà manuale tradizionale.
Invio di messaggi di notifica
Se la proprietà viene dichiarata in un tipo che eredita da ObservableRecipient, è possibile utilizzare l'attributo NotifyPropertyChangedRecipients per indicare al generatore di inserire anche il codice per inviare un messaggio di modifica della proprietà per la modifica della proprietà. Ciò consentirà ai destinatari registrati di reagire dinamicamente alla modifica. Vale a dire, considerare questo codice:
[ObservableProperty]
[NotifyPropertyChangedRecipients]
private string? name;
Ciò comporterà la generazione della proprietà seguente:
public string? Name
{
get => name;
set
{
string? oldValue = name;
if (SetProperty(ref name, value))
{
Broadcast(oldValue, value);
}
}
}
La chiamata generata Broadcast invierà quindi un nuovo PropertyChangedMessage<T> oggetto utilizzando l'istanza IMessenger in uso nel modello di visualizzazione corrente a tutti i sottoscrittori registrati.
Aggiunta di attributi personalizzati
In alcuni casi, potrebbe essere utile avere anche alcuni attributi personalizzati sulle proprietà generate. A tale scopo, è sufficiente usare la [property: ] destinazione negli elenchi di attributi sui campi con annotazioni e MVVM Toolkit invierà automaticamente tali attributi alle proprietà generate.
Si consideri ad esempio un campo simile al seguente:
[ObservableProperty]
[property: JsonRequired]
[property: JsonPropertyName("name")]
private string? username;
Verrà generata una Username proprietà, con questi due [JsonRequired] attributi e [JsonPropertyName("name")] su di esso. È possibile usare tutti gli elenchi di attributi destinati alla proprietà desiderata e tutti verranno inoltrati alle proprietà generate.
Esempi
- Vedere l'app di esempio (per più framework dell'interfaccia utente) per vedere MVVM Toolkit in azione.
- È anche possibile trovare altri esempi negli unit test.