Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O ObservableProperty tipo é um atributo que permite gerar propriedades observáveis a partir de campos anotados. O seu objetivo é reduzir significativamente a quantidade de boilerplate necessária para definir propriedades observáveis.
Observação
Para funcionar, os campos anotados precisam de estar numa classe parcial com a infraestrutura necessária INotifyPropertyChanged . Se o tipo estiver aninhado, todos os tipos na árvore sintáctica de declarações também devem ser anotados como parciais. Não o fazer resultará em erros de compilação, pois o gerador não poderá gerar uma declaração parcial diferente desse tipo com a propriedade observável solicitada.
APIs da plataforma:
ObservableProperty,NotifyPropertyChangedFor,NotifyDataErrorInfoNotifyCanExecuteChangedFor, ,NotifyPropertyChangedRecipients,ICommandIRelayCommandObservableValidator,PropertyChangedMessage<T>,IMessenger
Como funciona
O ObservableProperty atributo pode ser usado para anotar um campo num tipo parcial, da seguinte forma:
[ObservableProperty]
private string? name;
E gerará uma propriedade observável assim:
public string? Name
{
get => name;
set => SetProperty(ref name, value);
}
Também o fará com uma implementação otimizada, pelo que o resultado final será ainda mais rápido.
Observação
O nome da propriedade gerada será criado com base no nome do campo. O gerador assume que o campo é nomeado lowerCamel, _lowerCamel ou m_lowerCamel, e transformará isso para ser UpperCamel seguir as convenções de nomenclatura .NET corretas. A propriedade resultante terá sempre acessórios públicos, mas o campo pode ser declarado com qualquer visibilidade (private recomenda-se).
Execução de código após alterações
O código gerado é, na verdade, um pouco mais complexo do que isto, e a razão para isso é que também expõe alguns métodos que podes implementar para ligar à lógica de notificação e executar lógica adicional quando a propriedade está prestes a ser atualizada e logo a seguir, se necessário. Ou seja, o código gerado é na verdade semelhante a este:
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);
Isto permite-te implementar qualquer um desses métodos para injetar código adicional. Os dois primeiros são úteis sempre que quiseres executar alguma lógica que só precisa de referenciar o novo valor que a propriedade definiu. Os outros dois são úteis sempre que tens alguma lógica mais complexa que também tem de atualizar algum estado tanto no valor antigo como no novo que está a ser definido.
Por exemplo, aqui está um exemplo de como as duas primeiras sobrecargas podem ser usadas:
[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}");
}
E aqui está um exemplo de como as outras duas sobrecargas podem ser usadas:
[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;
}
}
Só podes implementar qualquer número de métodos entre os disponíveis, ou nenhum deles. Se não estiverem implementados (ou se apenas um estiver), a(s) chamada(s) inteira será simplesmente removida pelo compilador, pelo que não haverá qualquer impacto de desempenho nos casos em que esta funcionalidade adicional não seja necessária.
Observação
Os métodos gerados são métodos parciais sem implementação, o que significa que, se optar por os implementar, não pode especificar uma acessibilidade explícita para eles. Ou seja, as implementações destes métodos também devem ser declaradas como métodos justos partial , e terão sempre implicitamente acessibilidade privada. Tentar adicionar uma acessibilidade explícita (por exemplo, adicionar public ou private) resultará num erro, pois isso não é permitido em C#.
Notificação de propriedades dependentes
Imagine que tem uma FullName propriedade para a qual quer notificar sempre que Name mude. Podes fazer isso usando o NotifyPropertyChangedFor atributo, assim:
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
private string? name;
Isto resultará numa propriedade gerada equivalente a esta:
public string? Name
{
get => name;
set
{
if (SetProperty(ref name, value))
{
OnPropertyChanged("FullName");
}
}
}
Notificação de comandos dependentes
Imagine que tem um comando cujo estado de execução depende do valor dessa propriedade. Ou seja, sempre que a propriedade mudasse, o estado de execução do comando deveria ser invalidado e calculado novamente. Por outras palavras, ICommand.CanExecuteChanged deveria ser reativado. Pode conseguir isto usando o NotifyCanExecuteChangedFor atributo:
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(MyCommand))]
private string? name;
Isto resultará numa propriedade gerada equivalente a esta:
public string? Name
{
get => name;
set
{
if (SetProperty(ref name, value))
{
MyCommand.NotifyCanExecuteChanged();
}
}
}
Para que isto funcione, o comando alvo tem de ser uma propriedade qualquer IRelayCommand .
Solicitação de validação de propriedades
Se a propriedade for declarada num tipo que herda do ObservableValidator, também é possível anotá-la com quaisquer atributos de validação e depois pedir ao setter gerado que desencadeie a validação dessa propriedade. Isto pode ser conseguido com o NotifyDataErrorInfo atributo:
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[MinLength(2)] // Any other validation attributes too...
private string? name;
Isto resultará na geração da seguinte propriedade:
public string? Name
{
get => name;
set
{
if (SetProperty(ref name, value))
{
ValidateProperty(value, "Value2");
}
}
}
Essa chamada gerada ValidateProperty irá então validar a propriedade e atualizar o estado do ObservableValidator objeto, para que os componentes da interface possam reagir e mostrar quaisquer erros de validação adequadamente.
Observação
Por design, apenas os atributos do campo que herdam de ValidationAttribute serão encaminhados para a propriedade gerada. Isto é feito especificamente para suportar cenários de validação de dados. Todos os outros atributos do campo serão ignorados, pelo que atualmente não é possível adicionar atributos personalizados adicionais num campo e que também sejam aplicados à propriedade gerada. Se isso for necessário (por exemplo, para controlar a serialização), considere usar uma propriedade manual tradicional.
Envio de mensagens de notificação
Se a propriedade for declarada num tipo que herda de ObservableRecipient, pode usar o NotifyPropertyChangedRecipients atributo para instruir o gerador a inserir também código para enviar uma mensagem de alteração de propriedade para a alteração da propriedade. Isto permitirá que os destinatários registados reajam dinamicamente à alteração. Ou seja, considere este código:
[ObservableProperty]
[NotifyPropertyChangedRecipients]
private string? name;
Isto resultará na geração da seguinte propriedade:
public string? Name
{
get => name;
set
{
string? oldValue = name;
if (SetProperty(ref name, value))
{
Broadcast(oldValue, value);
}
}
}
Essa chamada gerada Broadcast enviará então uma nova PropertyChangedMessage<T> utilização da IMessenger instância em uso no modelo de visualização atual, para todos os subscritores registados.
Adição de atributos personalizados
Em alguns casos, pode ser útil também ter alguns atributos personalizados sobre as propriedades geradas. Para isso, pode simplesmente usar o [property: ] destino nas listas de atributos em vez de campos anotados, e o MVVM Toolkit encaminhará automaticamente esses atributos para as propriedades geradas.
Por exemplo, considere um campo como este:
[ObservableProperty]
[property: JsonRequired]
[property: JsonPropertyName("name")]
private string? username;
Isto gera uma Username propriedade, com esses dois [JsonRequired] e [JsonPropertyName("name")] atributos por cima. Pode usar quantas listas de atributos visam a propriedade quanto quiser, e todas serão encaminhadas para as propriedades geradas.
Exemplos
- Dá uma vista de olhos à aplicação de exemplo (para múltiplos frameworks de interface) para veres o MVVM Toolkit em ação.
- Também podes encontrar mais exemplos nos testes unitários.