Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Le ObservableProperty type est un attribut qui permet de générer des propriétés observables à partir de champs annotés. Son objectif est de réduire considérablement la quantité de code passe-partout nécessaire pour définir des propriétés observables.
Note
Pour fonctionner, les champs annotés doivent se trouver dans une classe partielle avec l’infrastructure nécessaire INotifyPropertyChanged . Si le type est imbriqué, tous les types de l’arborescence de syntaxe de déclaration doivent également être annotés comme partiels. Cela entraîne des erreurs de compilation, car le générateur ne pourra pas générer une déclaration partielle différente de ce type avec la propriété observable demandée.
API de plateforme :
ObservableProperty, ,NotifyPropertyChangedFor,NotifyCanExecuteChangedFor,NotifyDataErrorInfoNotifyPropertyChangedRecipientsICommandIRelayCommandObservableValidator,PropertyChangedMessage<T>,IMessenger
Fonctionnement
L’attribut ObservableProperty peut être utilisé pour annoter un champ dans un type partiel, comme suit :
[ObservableProperty]
private string? name;
Et il génère une propriété observable comme suit :
public string? Name
{
get => name;
set => SetProperty(ref name, value);
}
Il le fera également avec une implémentation optimisée, de sorte que le résultat final sera encore plus rapide.
Note
Le nom de la propriété générée est créé en fonction du nom du champ. Le générateur suppose que le champ est nommé lowerCamel, _lowerCamel ou m_lowerCamel, et qu’il le transformera comme UpperCamel pour suivre les conventions d’affectation de noms appropriées .NET. La propriété résultante aura toujours des accesseurs publics, mais le champ peut être déclaré avec n’importe quelle visibilité (private est recommandé).
Exécution du code lors des modifications
Le code généré est en fait un peu plus complexe que cela, et la raison en est qu’il expose également certaines méthodes que vous pouvez implémenter pour se connecter à la logique de notification, et exécuter une logique supplémentaire lorsque la propriété est sur le point d’être mise à jour et juste après sa mise à jour, si nécessaire. Autrement dit, le code généré est en fait similaire à ceci :
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);
Cela vous permet d’implémenter l’une de ces méthodes pour injecter du code supplémentaire. Les deux premières sont utiles chaque fois que vous souhaitez exécuter une logique qui doit uniquement référencer la nouvelle valeur sur laquelle la propriété a été définie. Les deux autres sont utiles chaque fois que vous avez une logique plus complexe qui doit également mettre à jour un état sur l’ancienne et la nouvelle valeur en cours de définition.
Par exemple, voici un exemple de la façon dont les deux premières surcharges peuvent être utilisées :
[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}");
}
Voici un exemple de la façon dont les deux autres surcharges peuvent être utilisées :
[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;
}
}
Vous êtes libre d’implémenter uniquement un certain nombre de méthodes parmi celles disponibles, ou aucune d’entre elles. S’ils ne sont pas implémentés (ou s’il n’en existe qu’un seul), la ou les appels entiers seront simplement supprimés par le compilateur, de sorte qu’il n’y aura aucun impact sur les performances pour les cas où cette fonctionnalité supplémentaire n’est pas requise.
Note
Les méthodes générées sont des méthodes partielles sans implémentation, ce qui signifie que si vous choisissez de les implémenter, vous ne pouvez pas spécifier d’accessibilité explicite pour eux. Autrement dit, les implémentations de ces méthodes doivent également être déclarées comme des méthodes uniquement partial , et elles auront toujours une accessibilité privée. La tentative d’ajout d’une accessibilité explicite (par exemple, l’ajout public ou private) entraîne une erreur, car elle n’est pas autorisée en C#.
Notification des propriétés dépendantes
Supposons que vous ayez une propriété FullName pour laquelle vous souhaitiez lever une notification chaque fois que Name change. Vous pouvez le faire à l’aide de l’attribut NotifyPropertyChangedFor , comme suit :
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
private string? name;
Cela entraîne une propriété générée équivalente à ceci :
public string? Name
{
get => name;
set
{
if (SetProperty(ref name, value))
{
OnPropertyChanged("FullName");
}
}
}
Notification des commandes dépendantes
Imaginez que vous aviez une commande dont l’état d’exécution dépendait de la valeur de cette propriété. Autrement dit, chaque fois que la propriété a changé, l’état d’exécution de la commande doit être invalidé et calculé à nouveau. En d’autres termes, ICommand.CanExecuteChanged devrait être relevé à nouveau. Pour ce faire, utilisez l’attribut NotifyCanExecuteChangedFor :
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(MyCommand))]
private string? name;
Cela entraîne une propriété générée équivalente à ceci :
public string? Name
{
get => name;
set
{
if (SetProperty(ref name, value))
{
MyCommand.NotifyCanExecuteChanged();
}
}
}
Pour que cela fonctionne, la commande cible doit être une IRelayCommand propriété.
Demande de validation de propriété
Si la propriété est déclarée dans un type qui hérite de ObservableValidator, il est également possible de l’annoter avec tous les attributs de validation, puis de demander au setter généré de déclencher la validation pour cette propriété. Vous pouvez effectuer cette opération avec l’attribut NotifyDataErrorInfo :
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[MinLength(2)] // Any other validation attributes too...
private string? name;
Cela entraîne la génération de la propriété suivante :
public string? Name
{
get => name;
set
{
if (SetProperty(ref name, value))
{
ValidateProperty(value, "Value2");
}
}
}
Cet appel généré ValidateProperty valide ensuite la propriété et met à jour l’état de l’objet afin que les composants de l’interface ObservableValidator utilisateur puissent y réagir et afficher les erreurs de validation appropriées.
Note
Par conception, seuls les attributs du champ qui héritent de ValidationAttribute seront transmis à la propriété générée. Cela s’effectue spécifiquement pour prendre en charge les scénarios de validation des données. Tous les autres attributs de champ sont ignorés. Il n’est donc pas possible actuellement d’ajouter des attributs personnalisés supplémentaires sur un champ et de les appliquer également à la propriété générée. Si cela est nécessaire (par exemple, pour contrôler la sérialisation), envisagez d’utiliser une propriété manuelle traditionnelle à la place.
Envoi de messages de notification
Si la propriété est déclarée dans un type qui hérite de ObservableRecipient, vous pouvez utiliser l’attribut NotifyPropertyChangedRecipients pour indiquer au générateur d’insérer également du code afin d’envoyer une notification de changement de propriété lorsque la propriété est modifiée. Cela permet aux destinataires inscrits de réagir dynamiquement à la modification. Autrement dit, tenez compte de ce code :
[ObservableProperty]
[NotifyPropertyChangedRecipients]
private string? name;
Cela entraîne la génération de la propriété suivante :
public string? Name
{
get => name;
set
{
string? oldValue = name;
if (SetProperty(ref name, value))
{
Broadcast(oldValue, value);
}
}
}
Cet appel Broadcast généré enverra ensuite un nouvel appel PropertyChangedMessage<T> à tous les abonnés enregistrés, à l’aide de l’instance IMessenger utilisée dans le viewmodel actuel.
Ajout d’attributs personnalisés
Dans certains cas, il peut être utile d’avoir également des attributs personnalisés sur les propriétés générées. Pour ce faire, vous pouvez simplement utiliser la [property: ] cible dans les listes d’attributs sur des champs annotés, et le kit de ressources MVVM transfère automatiquement ces attributs aux propriétés générées.
Par exemple, considérez un champ comme suit :
[ObservableProperty]
[property: JsonRequired]
[property: JsonPropertyName("name")]
private string? username;
Cela générera une propriété Username, avec les deux attributs [JsonRequired] et [JsonPropertyName("name")] appliqués à celle-ci. Vous pouvez utiliser autant de listes d’attributs ciblant la propriété que vous le souhaitez, et toutes seront transférées aux propriétés générées.
Exemples
- Consultez l’exemple d’application (pour plusieurs frameworks d’interface utilisateur) pour voir le kit de ressources MVVM en action.
- Vous trouverez également d’autres exemples dans les tests unitaires.