Événements personnalisés et accesseurs d'événement dans les composants Windows Runtime
La prise en charge du .NET Framework pour Windows Runtime facilite la déclaration des événements dans les composants Windows Runtime, car les différences entre le modèle d'événement Windows Runtime et le modèle d'événement .NET Framework sont masquées. Toutefois, lorsque vous déclarez des accesseurs d'événement personnalisés dans un composant Windows Runtime, vous devez suivre le modèle Windows Runtime.
Lorsque vous vous inscrivez pour gérer un événement dans Windows Runtime, l'accesseur add retourne un jeton. Pour annuler l'inscription, vous devez passer ce jeton à l'accesseur remove. Cela signifie que les accesseurs add et remove pour les événements Windows Runtime ont des signatures différentes des accesseurs auxquels vous êtes habitués.
Heureusement, les compilateurs C# et Visual Basic simplifient ce processus. Lorsque vous déclarez un événement avec les accesseurs personnalisés dans un composant Windows Runtime, les compilateurs utilisent automatiquement le modèle Windows Runtime. Par exemple, vous obtenez une erreur de compilation si l'accesseur add ne retourne pas de jeton. Deux types sont proposés par .NET Framework pour prendre en charge l'implémentation :
La structure EventRegistrationToken représente le jeton.
La classe EventRegistrationTokenTable<T> crée des jetons et met à jour un gère un mappage entre les jetons et les gestionnaires d'événements. L'argument de type générique est le type d'argument d'événement. Vous devez créer une instance de cette classe pour chaque événement, la première fois qu'un gestionnaire d'événements est inscrit à leur intention.
Le code suivant pour l'événement NumberChanged montre le modèle de base pour les événements Windows Runtime. Dans cet exemple, le constructeur de l'objet d'argument d'événement, NumberChangedEventArgs, prend un seul paramètre entier représentant la valeur numérique modifiée.
Notes
Il s'agit du même modèle que les compilateurs utilisent pour les événements ordinaires que vous déclarez dans un composant Windows Runtime.
private EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
m_NumberChangedTokenTable = null;
public event EventHandler<NumberChangedEventArgs> NumberChanged
{
add
{
return EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
.GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
.AddEventHandler(value);
}
remove
{
EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
.GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
.RemoveEventHandler(value);
}
}
internal void OnNumberChanged(int newValue)
{
EventHandler<NumberChangedEventArgs> temp =
EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
.GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
.InvocationList;
if (temp != null)
{
temp(this, new NumberChangedEventArgs(newValue));
}
}
Private m_NumberChangedTokenTable As _
EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs))
Public Custom Event NumberChanged As EventHandler(Of NumberChangedEventArgs)
AddHandler(ByVal handler As EventHandler(Of NumberChangedEventArgs))
Return EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
AddEventHandler(handler)
End AddHandler
RemoveHandler(ByVal token As EventRegistrationToken)
EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
RemoveEventHandler(token)
End RemoveHandler
RaiseEvent(ByVal sender As Class1, ByVal args As NumberChangedEventArgs)
Dim temp As EventHandler(Of NumberChangedEventArgs) = _
EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
InvocationList
If temp IsNot Nothing Then
temp(sender, args)
End If
End RaiseEvent
End Event
La méthode static (Shared en Visual Basic) GetOrCreateEventRegistrationTokenTable crée tardivement l'instance d'événement de l'objet EventRegistrationTokenTable<T>. Passez le champ de niveau classe qui contiendra l'instance de table de jeton à cette méthode. Si le champ est vide, la méthode crée la table, stocke une référence vers la table dans le champ et retourne une référence à la table. Si le champ contient déjà une référence de table de jeton, la méthode retourne simplement cette référence.
Important
Pour garantir la sécurité des threads, le champ qui contient l'instance EventRegistrationTokenTable<T> de l'événement doit être un champ de niveau classe. S'il s'agit d'un champ de niveau classe, la méthode GetOrCreateEventRegistrationTokenTable garantit que lorsque plusieurs threads tentent de créer la table de jeton, ils obtiennent tous la même instance de la table. Pour un événement donné, tous les appels à la méthode GetOrCreateEventRegistrationTokenTable doivent utiliser le même champ de niveau classe.
Le fait d'appeler la méthode GetOrCreateEventRegistrationTokenTable dans l'accesseur remove et dans la méthode RaiseEvent (méthode OnRaiseEvent en C#) garantit qu'aucune exception ne se produit si ces méthodes sont appelées avant que les délégués de gestionnaire d'événements n'aient été ajoutés.
Les autres membres de la classe EventRegistrationTokenTable<T> utilisés dans le modèle d'événement Windows Runtime incluent les éléments suivants :
La méthode AddEventHandler génère un jeton pour le délégué de gestionnaire d'événements, stocke le délégué dans la table, l'ajoute à la liste d'appel, puis retourne le jeton.
La surcharge de méthode RemoveEventHandler(EventRegistrationToken) supprime le délégué de la table et de la liste d'appel.
Notes
Les méthodes AddEventHandler et RemoveEventHandler(EventRegistrationToken) verrouillent la table afin de garantir la sécurité des threads.
La propriété InvocationList retourne un délégué qui inclut tous les gestionnaires d'événements qui sont actuellement inscrits pour gérer l'événement. Utilisez ce délégué pour déclencher l'événement, ou utilisez les méthodes de classe Delegate pour appeler des gestionnaires individuellement.
Notes
Nous vous recommandons de suivre le modèle indiqué dans l'exemple fourni précédemment dans cet article, et de copier le délégué dans une variable temporaire avant de l'appeler. Cela évite une condition de concurrence dans laquelle le thread supprime le dernier gestionnaire, et réduit ainsi le délégué à null juste avant qu'un autre thread tente d'appeler le délégué. Les délégués sont immuables, la copie est donc toujours valide.
Placez votre propre code dans les accesseurs si nécessaire. Si la sécurité des threads pose problème, vous devez fournir votre propre verrouillage pour votre code.
Utilisateurs C# : lorsque vous écrivez des accesseurs d'événement personnalisés dans le modèle d'événement Windows Runtime, le compilateur ne fournit pas les raccourcis de syntaxe habituels. Il génère des erreurs si vous utilisez le nom de l'événement dans votre code.
Utilisateurs de Visual Basic : dans .NET Framework, un événement est simplement un délégué multicast qui représente tous les gestionnaires d'événements inscrits. Le fait de déclencher l'événement signifie simplement appeler le délégué. La syntaxe Visual Basic masque généralement les interactions avec le délégué, et le compilateur copie le délégué avant de l'appeler, comme décrit dans la remarque sur la sécurité des threads. Lorsque vous créez un événement personnalisé dans un composant Windows Runtime, vous devez gérer le délégué directement. Cela signifie également que vous pouvez, par exemple, utiliser la méthode MulticastDelegate.GetInvocationList pour obtenir un tableau qui contient un délégué distinct pour chaque gestionnaire d'événements, si vous voulez appeler des gestionnaires séparément.
Voir aussi
Tâches
Comment : implémenter des accesseurs d'événement personnalisés (Guide de programmation C#)
Référence
Événements (Guide de programmation C#)
Autres ressources
Prise en charge .NET Framework pour les applications Windows Store et Windows Runtime