Vue d'ensemble des événements routés

Cette rubrique décrit le concept d’événements routés dans WPF (Windows Presentation Foundation). Elle définit les termes utilisés pour les événements routés, elle explique comment ces événements sont routés via une arborescence d’éléments, elle résume la façon dont sont gérés les événements routés et elle explique comment créer ses propres événements routés personnalisés.

Prérequis

Cette rubrique part du principe que vous avez des connaissances de base sur le Common Language Runtime (CLR) et la programmation orientée objet, ainsi que sur le concept de la façon dont les relations entre les éléments WPF peuvent être conceptualisées en tant qu’arborescence. Pour suivre les exemples de cette rubrique, vous devez également comprendre le langage XAML (Extensible Application Markup Language) et savoir comment écrire des applications ou des pages WPF de base. Pour plus d’informations, consultez Procédure pas à pas : Ma première application de bureau WPF et XAML dans WPF.

Qu’est-ce qu’un événement routé ?

Les événements routés peuvent être compris d’un point de vue fonctionnel et du point de vue de l’implémentation. Les deux définitions sont présentées ici pour une meilleure compréhension du concept.

Définition du point de vue fonctionnel : un événement routé est un type d’événement qui peut appeler des gestionnaires sur plusieurs écouteurs d’une arborescence d’éléments, au lieu d’appeler uniquement sur l’objet qui a déclenché l’événement.

Définition de l’implémentation : un événement routé est un événement CLR soutenu par une instance de la RoutedEvent classe et traité par le système d’événements Windows Presentation Foundation (WPF).

Une application WPF classique contient de nombreux éléments. Que ce soit créé dans le code ou déclaré en XAML, ces éléments existent dans une relation d’arborescence d’éléments entre eux. L’itinéraire de l’événement peut prendre deux directions différentes, en fonction de la définition de l’événement. Toutefois, l’itinéraire part en général de l’élément source et se propage ensuite vers le haut dans l’arborescence d’éléments, jusqu’à atteindre la racine de l’arborescence d’éléments (en général, une page ou une fenêtre). Le concept de propagation peut vous être familier si vous avez déjà travaillé avec le modèle d’objet DHTML.

Prenons l’arborescence d’éléments suivante :

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

Cette arborescence d’éléments produit ce qui suit :

Yes, No, and Cancel buttons

Dans cette arborescence d’éléments simplifiée, la source d’un événement est l’un Click des Button éléments, et le premier Button élément qui a été cliqué est le premier élément qui a la possibilité de gérer l’événement. Toutefois, si aucun gestionnaire n’est attaché aux Button actes sur l’événement, l’événement est en bulle vers le haut vers le Button parent dans l’arborescence d’éléments, qui est le StackPanel. Potentiellement, les bulles d’événements à Border, puis au-delà de la racine de la page de l’arborescence d’éléments (non affichée).

En d’autres termes, l’itinéraire d’événement pour cet Click événement est le suivant :

Button--StackPanel>--Border>-...>

Scénarios de premier niveau pour les événements routés

Voici un bref résumé des scénarios qui ont motivé le concept d’événement routé et pourquoi un événement CLR classique n’était pas adéquat pour ces scénarios :

Composition et encapsulation des contrôles : différents contrôles dans WPF ont un con riche mode tente l. Par exemple, vous pouvez placer une image à l’intérieur d’un Button, qui étend efficacement l’arborescence visuelle du bouton. Toutefois, l’image ajoutée ne doit pas briser le comportement de test de positionnement qui provoque la réponse d’un bouton à un Click de son contenu, même si l’utilisateur clique sur des pixels qui font partie techniquement de l’image.

Points de pièce jointe du gestionnaire singulier : dans Windows Forms, vous devez attacher le même gestionnaire plusieurs fois pour traiter les événements susceptibles d’être déclenchés à partir de plusieurs éléments. Les événements routés vous permettent d’attacher ce gestionnaire une seule fois, comme dans l’exemple précédent, et d’utiliser la logique du gestionnaire pour déterminer d’où provient l’événement, si nécessaire. Par exemple, il peut s’agir du gestionnaire pour le code XAML précédemment affiché :

private void CommonClickHandler(object sender, RoutedEventArgs e)
{
  FrameworkElement feSource = e.Source as FrameworkElement;
  switch (feSource.Name)
  {
    case "YesButton":
      // do something here ...
      break;
    case "NoButton":
      // do something ...
      break;
    case "CancelButton":
      // do something ...
      break;
  }
  e.Handled=true;
}
Private Sub CommonClickHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
  Dim feSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
  Select Case feSource.Name
    Case "YesButton"
      ' do something here ...
    Case "NoButton"
      ' do something ...
    Case "CancelButton"
      ' do something ...
  End Select
  e.Handled=True
End Sub

Gestion des classes : Les événements routés permettent l’utilisation d’un gestionnaire statique défini par la classe. Ce gestionnaire de classe a l’opportunité de gérer un événement avant les autres gestionnaires d’instance attachés.

Référencement d’un événement sans réflexion : Certaines techniques de code et de balisage nécessitent un moyen d’identifier les événements. Un événement routé crée un RoutedEvent champ en tant qu’identificateur, qui fournit une technique d’identification d’événement robuste qui ne nécessite pas de réflexion statique ou au moment de l’exécution.

Implémentation des événements routés

Un événement routé est un événement CLR soutenu par une instance de la RoutedEvent classe et inscrit auprès du système d’événements WPF. L’instance RoutedEvent obtenue à partir de l’inscription est généralement conservée en tant que publicstaticreadonly membre de champ de la classe qui inscrit et donc « possède » l’événement routé. La connexion à l’événement CLR nommé identiquement (ce qui est parfois appelé événement « wrapper ») est effectuée en substituant les implémentations et remove les add implémentations de l’événement CLR. En règle générale, add et remove sont conservés comme des valeurs implicites par défaut qui utilisent la syntaxe d’événement spécifique au langage pour ajouter et supprimer des gestionnaires de cet événement. Le mécanisme de stockage et de connexion des événements routés est conceptuellement similaire à la façon dont une propriété de dépendance est une propriété CLR sauvegardée par la DependencyProperty classe et inscrite auprès du système de propriétés WPF.

L’exemple suivant montre la déclaration d’un événement routé personnalisé Tap , y compris l’inscription et l’exposition du RoutedEvent champ d’identificateur et les addremove implémentations de l’événement Tap CLR.

public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    "Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));

// Provide CLR accessors for the event
public event RoutedEventHandler Tap
{
        add { AddHandler(TapEvent, value); }
        remove { RemoveHandler(TapEvent, value); }
}
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent("Tap", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(MyButtonSimple))

' Provide CLR accessors for the event
Public Custom Event Tap As RoutedEventHandler
    AddHandler(ByVal value As RoutedEventHandler)
        Me.AddHandler(TapEvent, value)
    End AddHandler

    RemoveHandler(ByVal value As RoutedEventHandler)
        Me.RemoveHandler(TapEvent, value)
    End RemoveHandler

    RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Me.RaiseEvent(e)
    End RaiseEvent
End Event

Gestionnaires d’événements routés et XAML

Pour ajouter un gestionnaire pour un événement à l’aide de XAML, vous déclarez le nom de l’événement en tant qu’attribut sur l’élément qui est un écouteur d’événements. La valeur de l’attribut est le nom de votre méthode de gestionnaire implémentée, qui doit se trouver dans la classe partielle du fichier code-behind.

<Button Click="b1SetColor">button</Button>

La syntaxe XAML pour l’ajout de gestionnaires d’événements CLR standard est la même pour l’ajout de gestionnaires d’événements routés, car vous ajoutez vraiment des gestionnaires au wrapper d’événements CLR, qui a une implémentation d’événement routée en dessous. Pour plus d’informations sur l’ajout de gestionnaires d’événements en XAML, consultez XAML dans WPF.

Stratégies de routage

Les événements routés utilisent l’une des trois stratégies de routage suivantes :

  • Propagation : Les gestionnaires d’événements de la source d’événements sont appelés. L’événement routé est acheminé ensuite vers des éléments parents successifs jusqu’à atteindre la racine de l’arborescence d’éléments. La plupart des événements routés utilisent la stratégie de routage par propagation. Les événements routés par propagation sont généralement utilisés pour signaler des entrées ou des changements d’état à partir de contrôles distincts ou d’autres éléments d’interface utilisateur.

  • Direct : Seul l’élément source a l’opportunité d’appeler les gestionnaires en réponse. Cela est analogue au « routage » que Windows Forms utilise pour les événements. Toutefois, contrairement à un événement CLR standard, les événements routés directs prennent en charge la gestion des classes (la gestion des classes est expliquée dans une section à venir) et peuvent être utilisés par et EventTriggerpar EventSetter .

  • Tunneling : Au début, les gestionnaires d’événements situés à la racine de l’arborescence d’éléments sont appelés. L’événement routé passe ensuite par des éléments enfants successifs pour aller vers l’élément de nœud correspondant à la source de l’événement routé (l’élément qui a déclenché l’événement routé). Les événements routés de tunneling sont souvent utilisés ou gérés dans le cadre de la composition d’un contrôle, de telle manière que les événements issus de parties composites peuvent être délibérément supprimés ou remplacés par des événements spécifiques au contrôle complet. Les événements d’entrée fournis dans WPF sont souvent implémentés en tant que paire tunneling/bubbling. Les événements de tunneling sont parfois appelés événements Preview en raison d’une convention de nommage utilisée pour les paires.

Pourquoi utiliser des événements routés ?

En tant que développeur d’applications, il n’est pas toujours nécessaire de savoir que l’événement que vous gérez est implémenté comme un événement routé. Les événements routés présentent un comportement particulier, mais ce comportement est en grande partie invisible si vous gérez l’événement sur l’élément à partir duquel il a été déclenché.

Les événements routés deviennent puissants lorsqu’ils sont utilisés dans l’un des scénarios suivants : définition de gestionnaires communs au niveau d’une racine commune, composition de votre propre contrôle ou définition de votre propre classe de contrôle personnalisé.

Les écouteurs et les sources d’événements routés n’ont pas besoin de partager un événement commun dans leur hiérarchie. Tout UIElement ou ContentElement peut être un écouteur d’événements pour n’importe quel événement routé. Par conséquent, vous pouvez utiliser l’ensemble complet d’événements routés disponibles dans l’ensemble de l’API de travail en tant qu'« interface » conceptuelle dans laquelle des éléments disparates dans l’application peuvent échanger des informations sur les événements. Ce concept d’« interface » pour les événements routés s’applique plus particulièrement aux événements d’entrée.

Les événements routés peuvent également servir à communiquer au sein de l’arborescence d’éléments, car les données de l’événement sont propagées vers chaque élément rencontré sur l’itinéraire. Si un élément change les données d’événement, ce changement est disponible pour l’élément suivant dans l’itinéraire.

En dehors de l’aspect de routage, il existe deux autres raisons pour lesquelles un événement WPF donné peut être implémenté en tant qu’événement routé au lieu d’un événement CLR standard. Si vous implémentez vos propres événements, vous pouvez également prendre en compte ces principes :

  • Certaines fonctionnalités de style et de création de modèles WPF, telles que EventSetter l’événement référencé, EventTrigger doivent être un événement routé. Il s’agit du scénario d’identificateur d’événement mentionné précédemment.

  • Les événements routés prennent en charge un mécanisme de gestion de classe par lequel la classe peut spécifier des méthodes statiques qui ont l’opportunité de gérer des événements routés avant que les gestionnaires d’instance inscrits puissent y accéder. Ceci est très utile pour la conception de contrôles, car la classe peut appliquer des comportements de classe pilotés par les événements qui ne peuvent pas être supprimés par inadvertance lorsque vous gérez un événement sur une instance.

Toutes les considérations ci-dessus sont abordées dans une autre section de cette rubrique.

Ajout et implémentation d’un gestionnaire d’événements pour un événement routé

Pour ajouter un gestionnaire d’événements en XAML, vous ajoutez simplement le nom d’événement à un élément en tant qu’attribut et définissez la valeur d’attribut comme nom du gestionnaire d’événements qui implémente un délégué approprié, comme dans l’exemple suivant.

<Button Click="b1SetColor">button</Button>

b1SetColor est le nom du gestionnaire implémenté qui contient le code qui gère l’événement Click . b1SetColor doit avoir la même signature que le RoutedEventHandler délégué, qui est le délégué du gestionnaire d’événements pour l’événement Click . Le premier paramètre de tous les délégués de gestionnaires d’événements routés spécifie l’élément auquel le gestionnaire d’événements est ajouté, et le deuxième paramètre spécifie les données de l’événement.

void b1SetColor(object sender, RoutedEventArgs args)
{
  //logic to handle the Click event
}
Private Sub b1SetColor(ByVal sender As Object, ByVal args As RoutedEventArgs)
  'logic to handle the Click event
End Sub

RoutedEventHandler est le délégué de gestionnaire d’événements routés de base. Pour les événements routés qui conviennent spécialement à certains contrôles ou scénarios, les délégués à utiliser avec les gestionnaires d’événements routés peuvent également devenir plus spécialisés, de manière à pouvoir transmettre des données d’événement spécialisées. Par exemple, dans un scénario d’entrée courant, vous pouvez gérer un DragEnter événement routé. Votre gestionnaire doit implémenter le DragEventHandler délégué. En utilisant le délégué le plus spécifique, vous pouvez traiter le DragEventArgs gestionnaire et lire la Data propriété, qui contient la charge utile du Presse-papiers de l’opération de glisser.

Pour obtenir un exemple complet d’ajout d’un gestionnaire d’événements à un élément à l’aide de XAML, consultez Gérer un événement routé.

L’ajout d’un gestionnaire pour un événement routé dans une application créée dans du code est une opération simple. Les gestionnaires d’événements routés peuvent toujours être ajoutés via une méthode AddHandler d’assistance (qui est la même méthode que les appels de stockage existants pour add.) Toutefois, les événements routés WPF existants ont généralement des implémentations de sauvegarde et remove une add logique qui permettent aux gestionnaires d’événements routés d’être ajoutés par une syntaxe d’événement spécifique au langage, qui est plus intuitive que la méthode d’assistance. Voici un exemple d’utilisation de la méthode d’assistance :

void MakeButton()
 {
     Button b2 = new Button();
     b2.AddHandler(Button.ClickEvent, new RoutedEventHandler(Onb2Click));
 }
 void Onb2Click(object sender, RoutedEventArgs e)
 {
     //logic to handle the Click event
 }
Private Sub MakeButton()
     Dim b2 As New Button()
     b2.AddHandler(Button.ClickEvent, New RoutedEventHandler(AddressOf Onb2Click))
End Sub
 Private Sub Onb2Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
     'logic to handle the Click event     
 End Sub

L’exemple suivant montre la syntaxe de l’opérateur C# (Visual Basic a une syntaxe d’opérateur légèrement différente en raison de sa gestion de la déréférencement) :

void MakeButton2()
{
  Button b2 = new Button();
  b2.Click += new RoutedEventHandler(Onb2Click2);
}
void Onb2Click2(object sender, RoutedEventArgs e)
{
  //logic to handle the Click event
}
Private Sub MakeButton2()
  Dim b2 As New Button()
  AddHandler b2.Click, AddressOf Onb2Click2
End Sub
Private Sub Onb2Click2(ByVal sender As Object, ByVal e As RoutedEventArgs)
  'logic to handle the Click event     
End Sub

Pour obtenir un exemple montrant comment ajouter un gestionnaire d’événements dans le code, consultez Ajouter un gestionnaire d’événements à l’aide de code.

Si vous utilisez Visual Basic, vous pouvez également utiliser l’mot clé Handles pour ajouter des gestionnaires dans le cadre des déclarations de gestionnaire. Pour plus d’informations, consultez Gestion des événements Visual Basic et WPF.

Concept du Handled

Tous les événements routés partagent une classe de base de données d’événements commune, RoutedEventArgs. RoutedEventArgs définit la Handled propriété, qui prend une valeur booléenne. L’objectif de la Handled propriété est d’activer n’importe quel gestionnaire d’événements le long de l’itinéraire pour marquer l’événement routé comme géré, en définissant la valeur sur Handledtrue. Après avoir été traitées par le gestionnaire au niveau d’un élément de l’itinéraire, les données d’événement partagées sont à nouveau signalées à chaque écouteur se trouvant sur l’itinéraire.

La valeur d’impact Handled sur la façon dont un événement routé est signalé ou traité au fur et à mesure qu’il se déplace plus loin le long de l’itinéraire. S’il Handled se trouve true dans les données d’événement pour un événement routé, les gestionnaires qui écoutent cet événement routé sur d’autres éléments ne sont généralement plus appelés pour cette instance d’événement particulière. Cela est vrai à la fois pour les gestionnaires attachés en XAML et pour les gestionnaires ajoutés par des syntaxes de pièces jointes spécifiques au langage, telles que += ou Handles. Pour les scénarios de gestionnaire les plus courants, le marquage d’un événement comme géré en définissant Handled sur true « arrêter » le routage pour un itinéraire de tunneling ou un itinéraire de bouclage, ainsi que pour tous les événements gérés à un point de l’itinéraire par un gestionnaire de classes.

Toutefois, il existe un mécanisme « handledEventsToo » dans lequel les écouteurs peuvent toujours exécuter des gestionnaires en réponse aux événements routés où Handled se trouvent true les données d’événement. En d’autres termes, l’itinéraire d’événement n’est pas réellement arrêté quand les données d’événement sont marquées comme gérées. Vous ne pouvez utiliser le mécanisme handledEventsToo que dans le code, ou dans un EventSetter:

  • Dans le code, au lieu d’utiliser une syntaxe d’événement spécifique au langage qui fonctionne pour les événements CLR généraux, appelez la méthode AddHandler(RoutedEvent, Delegate, Boolean) WPF pour ajouter votre gestionnaire. Définissez la valeur de handledEventsToo sur true.

  • Dans un EventSetter, définissez l’attribut HandledEventsToo sur true.

Outre le comportement généré Handled par l’état dans les événements routés, le concept d’impact Handled sur la façon dont vous devez concevoir votre application et écrire le code du gestionnaire d’événements. Vous pouvez conceptualiser Handled comme étant un protocole simple exposé par des événements routés. Exactement la façon dont vous utilisez ce protocole est à vous, mais la conception conceptuelle de la façon dont la valeur Handled est destinée à être utilisée est la suivante :

  • Si un événement routé est marqué comme géré, il n’a pas besoin d’être géré de nouveau par d’autres éléments sur l’itinéraire.

  • Si un événement routé n’est pas marqué comme géré, d’autres écouteurs qui étaient précédemment le long de l’itinéraire ont choisi de ne pas inscrire un gestionnaire, ou les gestionnaires qui ont été inscrits ont choisi de ne pas manipuler les données d’événement et définis Handled sur true. (Ou bien sûr, il est possible que l’écouteur actuel soit le premier point de la route.) Les gestionnaires sur l’écouteur actuel ont désormais trois cours d’action possibles :

    • Ne rien faire. L’événement reste non géré et l’événement est dirigé vers l’écouteur suivant.

    • Exécuter du code en réponse à l’événement, mais décider que l’action entreprise n’était pas suffisamment importante pour garantir le marquage de l’événement comme géré. L’événement est dirigé vers l’écouteur suivant.

    • Exécuter du code en réponse à l’événement. Marquer l’événement comme géré dans les données d’événement passées au gestionnaire, car l’action entreprise est jugée suffisamment importante pour garantir le marquage de l’événement comme géré. L’événement est toujours acheminé vers l’écouteur suivant, mais avec Handled=true ses données d’événement, seuls handledEventsToo les écouteurs ont la possibilité d’appeler d’autres gestionnaires.

Cette conception conceptuelle est renforcée par le comportement de routage mentionné précédemment : il est plus difficile (bien que toujours possible dans le code ou les styles) d’attacher des gestionnaires pour les événements routés qui sont appelés même si un gestionnaire précédent le long de l’itinéraire a déjà défini Handledtruesur .

Pour plus d’informations sur Handledla gestion des classes d’événements routés et les recommandations relatives au moment où il convient de marquer un événement Handledrouté comme , consultez Marquage des événements routés en tant que handle et gestion des classes.

Dans les applications, il est assez courant de gérer un événement routé par propagation sur l’objet qui l’a déclenché, sans se soucier des caractéristiques de routage de l’événement. Toutefois, il est recommandé de toujours marquer l’événement routé comme géré (handled) dans les données d’événement, afin d’éviter des effets secondaires si un élément situé plus haut dans l’arborescence d’éléments comportait également un gestionnaire attaché pour le même événement routé.

Gestionnaires de classe

Si vous définissez une classe qui dérive d’une certaine façon, DependencyObjectvous pouvez également définir et attacher un gestionnaire de classes pour un événement routé qui est un membre d’événement déclaré ou hérité de votre classe. Les gestionnaires de classe sont appelés avant les gestionnaires d’écouteur d’instance qui sont attachés à une instance de cette classe, chaque fois qu’un événement routé atteint une instance d’élément sur son itinéraire.

Certains contrôles WPF ont une gestion de classe inhérente pour certains événements routés. Ceci peut donner l’apparence que l’événement routé n’a même pas été déclenché, alors qu’en réalité il est géré par la classe, et peut encore potentiellement être géré par vos gestionnaires d’instance si vous utilisez certaines techniques. En outre, plusieurs classes et contrôles de base exposent des méthodes virtuelles qui peuvent être utilisées pour substituer le comportement de gestion de classe. Pour plus d’informations sur la façon de contourner la gestion de classe et sur la définition de votre propre gestion de classe dans une classe personnalisée, consultez Marquage des événements routés comme gérés et gestion de classe.

Événements attachés dans WPF

Le langage XAML définit également un type spécial d’événement appelé événement attaché. Un événement attaché permet d’ajouter le gestionnaire d’un événement particulier à un élément arbitraire. L’élément qui gère l’événement n’a pas besoin de définir ou d’hériter de l’événement attaché. En outre, ni l’objet qui déclenche potentiellement l’événement, ni l’instance de gestion de destination ne doivent définir ou « posséder » cet événement comme un membre de classe.

Le système d’entrée WPF utilise largement les événements attachés. Cependant, presque tous ces événements attachés sont transférés via des éléments de base. Les événements d’entrée apparaissent alors comme des événements routés non attachés équivalents qui sont membres de la classe d’élément de base. Par exemple, l’événement Mouse.MouseDown attaché sous-jacent peut être plus facilement géré sur n’importe quel élément donné UIElementMouseDownUIElement au lieu de traiter la syntaxe d’événement attachée en XAML ou en code.

Pour plus d’informations sur les événements joints dans WPF, consultez Vue d’ensemble des événements joints.

Noms qualifiés d’événements en XAML

Une autre utilisation de la syntaxe qui ressemble à la syntaxe d’événement attaché typename.eventname, mais qui n’est pas à proprement parler une utilisation d’événement attaché est lorsque vous attachez des gestionnaires pour les événements routés qui sont déclenchés par des éléments enfants. Vous attachez les gestionnaires à un parent commun pour tirer parti du routage d’événements, même si le parent commun ne dispose pas de l’événement routé nécessaire comme membre. Regardons de nouveau cet exemple :

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

Ici, l’écouteur d’élément parent où le gestionnaire est ajouté est un StackPanel. Toutefois, il ajoute un gestionnaire pour un événement routé qui a été déclaré et sera déclenché par la Button classe (ButtonBase en fait, mais disponible pour Button l’héritage). Button « possède » l’événement, mais le système d’événements routé permet aux gestionnaires pour tout événement routé d’être attaché à n’importe quel UIElement écouteur ou ContentElement écouteur d’instance qui pourrait autrement attacher des écouteurs pour un événement CLR (Common Language Runtime). L’espace de noms xmlns par défaut pour ces noms d’attributs d’événements qualifiés est généralement l’espace de noms xmlns WPF par défaut, mais vous pouvez également spécifier des espaces de noms préfixés pour les événements routés personnalisés. Pour plus d’informations sur xmlns, consultez Espaces de noms XAML et mappage d’espace de noms pour XAML WPF.

Événements d’entrée WPF

Une application fréquente d’événements routés au sein de la plateforme WPF est destinée aux événements d’entrée. Dans WPF, les noms d’événements routés par tunneling sont précédés du mot « Aperçu » par convention. Les événements d’entrée vont souvent par paires, l’un étant l’événement de propagation et l’autre, l’événement de tunneling. Par exemple, l’événement KeyDown et l’événement PreviewKeyDown ont la même signature, l’ancien étant l’événement d’entrée en cours et celui qui est l’événement d’entrée de tunneling. Parfois, les événements d’entrée ont uniquement une version par propagation, ou uniquement une version à routage direct. Dans la documentation, les rubriques relatives aux événements routés font référence à des événements routés similaires avec d’autres stratégies de routage, et les sections des pages concernant les références managées expliquent plus en détail la stratégie de routage de chaque événement routé.

Les événements d’entrée WPF fournis en paires sont implémentés afin qu’une seule action utilisateur à partir de l’entrée, telle qu’une pression sur un bouton de souris, déclenche les deux événements routés de la paire dans la séquence. Tout d’abord, l’événement de tunneling est déclenché et suit son itinéraire. Ensuite, l’événement de propagation est déclenché et suit son itinéraire. Les deux événements partagent littéralement la même instance de données d’événement, car l’appel RaiseEvent de méthode dans la classe d’implémentation qui déclenche l’événement de bubbling écoute les données d’événement à partir de l’événement de tunneling et les réutilise dans le nouvel événement déclenché. Les écouteurs qui comportent des gestionnaires pour l’événement de tunneling sont les premiers à pouvoir marquer l’événement routé comme géré (d’abord les gestionnaires de classe, ensuite les gestionnaires d’instance). Si un élément a marqué l’événement routé comme géré sur l’itinéraire de tunneling, les données d’événement déjà gérées sont envoyées à l’événement de propagation, et les gestionnaires standard attachés pour les événements d’entrée de propagation équivalents ne sont pas appelés. Vu de l’extérieur, il peut sembler que l’événement de propagation géré n’a pas encore été déclenché. Ce comportement de gestion est utile pour la composition de contrôles, où vous pouvez souhaiter que tous les événements d’entrée basés sur les tests d’atteinte ou le focus soient signalés par votre dernier contrôle, plutôt que par ses parties composites. Le dernier élément de contrôle est plus proche de la racine dans la composition et peut donc être le premier à gérer la classe de l’événement de tunneling et éventuellement à « remplacer » l’événement routé par un événement plus spécifique au contrôle dans le code qui stocke la classe de contrôle.

En guise d’illustration du traitement des événements d’entrée, prenez l’exemple d’événement d’entrée suivant. Dans l’arborescence suivante, leaf element #2 est la source d’un PreviewMouseDown événement et d’un MouseDown événement :

Event routing diagram

L’ordre de traitement des événements est le suivant :

  1. PreviewMouseDown (tunneling) sur l’élément racine.

  2. PreviewMouseDown (tunneling) sur l’élément intermédiaire numéro 1.

  3. PreviewMouseDown (tunneling) sur l’élément source numéro 2.

  4. MouseDown (propagation) sur l’élément source numéro 2.

  5. MouseDown (propagation) sur l’élément intermédiaire numéro 1.

  6. MouseDown (propagation) sur l’élément racine.

Un délégué de gestionnaire d’événements routés fournit des références à deux objets : l’objet qui a déclenché l’événement et l’objet à partir duquel le gestionnaire a été appelé. L’objet à partir duquel le gestionnaire a été appelé est celui signalé par le paramètre sender. L’objet où l’événement a été déclenché pour la première fois est signalé par la Source propriété dans les données d’événement. Un événement routé peut toujours être déclenché et géré par le même objet, auquel cas sender et Source sont identiques (c’est le cas avec les étapes 3 et 4 dans la liste d’exemples de traitement d’événements).

En raison du tunneling et de la bulle, les éléments parents reçoivent des événements d’entrée où il Source s’agit de l’un de leurs éléments enfants. Quand il est important de savoir quel est l’élément source, vous pouvez identifier l’élément source en accédant à la Source propriété.

En règle générale, une fois l’événement d’entrée marqué Handled, d’autres gestionnaires ne sont pas appelés. En outre, vous devez marquer les événements d’entrée comme gérés dès qu’un gestionnaire est appelé pour traiter votre gestion logique (et spécifique à l’application) de la signification de l’événement d’entrée.

L’exception à cette instruction générale sur Handled l’état est que les gestionnaires d’événements d’entrée inscrits pour ignorer Handled délibérément l’état des données d’événement sont toujours appelés le long de l’une ou l’autre route. Pour plus d’informations, consultez Événements Preview ou Marquage des événements routés comme gérés et gestion de classe.

Le modèle de données d’événement partagé entre les événements de tunneling et de propagation, avec le déclenchement séquentiel de l’événement de tunneling puis de l’événement de propagation, est un concept qui n’est pas valable pour tous les événements routés. Ce comportement est spécifiquement implémenté par la façon dont les appareils d’entrée WPF choisissent de déclencher et de connecter les paires d’événements d’entrée. L’implémentation de vos propres événements d’entrée est un scénario avancé. Toutefois, vous pouvez choisir de suivre ce modèle pour vos propres événements d’entrée.

Certaines classes choisissent de gérer la classe de certains événements d’entrée, généralement dans le but de redéfinir la signification d’un événement d’entrée piloté par l’utilisateur dans le contrôle, et dans le but de déclencher un nouvel événement. Pour plus d’informations, consultez Marquage des événements routés comme gérés et gestion de classe.

Pour plus d’informations sur les entrées et la façon dont elles interagissent avec les événements dans des scénarios d’application standard, consultez Vue d’ensemble des entrées.

EventSetters et EventTriggers

Dans les styles, vous pouvez inclure une syntaxe de gestion des événements XAML prédéclarée dans le balisage à l’aide d’un EventSetter. Lorsque le style est appliqué, le gestionnaire référencé est ajouté à l’instance à laquelle est appliqué le style. Vous pouvez déclarer un EventSetter seul événement routé. Voici un exemple. Notez que la méthode b1SetColor référencée ici se trouve dans un fichier code-behind.

<StackPanel
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.EventOvw2"
  Name="dpanel2"
  Initialized="PrimeHandledToo"
>
  <StackPanel.Resources>
    <Style TargetType="{x:Type Button}">
      <EventSetter Event="Click" Handler="b1SetColor"/>
    </Style>
  </StackPanel.Resources>
  <Button>Click me</Button>
  <Button Name="ThisButton" Click="HandleThis">
    Raise event, handle it, use handled=true handler to get it anyway.
  </Button>
</StackPanel>

L’avantage obtenu ici est que le style est susceptible de contenir une grande quantité d’autres informations qui pourraient s’appliquer à n’importe quel bouton dans votre application, et que le fait de faire partie de ce style favorise la EventSetter réutilisation du code même au niveau du balisage. En outre, un EventSetter nom de méthode abstraite pour les gestionnaires d’une étape plus loin de l’application générale et du balisage de page.

Une autre syntaxe spécialisée qui combine l’événement routé et les fonctionnalités d’animation de WPF est un EventTrigger. Comme avec EventSetter, seuls les événements routés peuvent être utilisés pour un EventTrigger. En règle générale, un EventTrigger est déclaré dans le cadre d’un style, mais il EventTrigger peut également être déclaré sur des éléments de niveau page dans le cadre de la Triggers collection, ou dans un ControlTemplate. Une EventTrigger option vous permet de spécifier un Storyboard qui s’exécute chaque fois qu’un événement routé atteint un élément dans son itinéraire qui déclare un EventTrigger événement pour cet événement. L’avantage d’une EventTrigger gestion juste de l’événement et de son démarrage d’un storyboard existant est qu’un EventTrigger meilleur contrôle sur le storyboard et son comportement d’exécution. Pour plus d’informations, consultez Utiliser des déclencheurs d’événements pour contrôler un storyboard après son démarrage.

Informations complémentaires sur les événements routés

Cette rubrique traite principalement des concepts de base relatifs aux événements routés, et explique comment et à quel moment répondre aux événements routés qui sont déjà présents dans les différents éléments et contrôles de base. Toutefois, vous pouvez créer vos propres événements routés dans votre classe personnalisée, ainsi que toute la prise en charge nécessaire, telle que les classes et les délégués spécialisés de données d’événement. Le propriétaire de l’événement routé peut être n’importe quelle classe, mais les événements routés doivent être déclenchés par et gérés par UIElement des classes dérivées ou ContentElement par des classes dérivées afin d’être utiles. Pour plus d’informations sur les événements personnalisés, consultez Créer un événement routé personnalisé.

Voir aussi