Partager via


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

Cette rubrique décrit le concept d'événements routés dans Windows Presentation Foundation (WPF). Elle définit la terminologie liée aux événements routés, explique le routage des événements routés dans une arborescence d'éléments, fournit un résumé de la gestion des événements routés et présente la création d'événements routés personnalisés.

Cette rubrique comprend les sections suivantes.

  • Composants requis
  • Qu'est-ce qu'un événement routé ?
  • Stratégies de routage
  • Pourquoi utiliser des événements routés ?
  • Ajout et implémentation d'un gestionnaire d'événements pour un événement routé
  • Gestionnaires de classe
  • Événements attachés dans WPF
  • Noms d'événements qualifiés en XAML
  • Événements d'entrée WPF
  • EventSetters et EventTriggers
  • Informations complémentaires sur les événements routés
  • Rubriques connexes

Composants requis

Cette rubrique suppose que vous disposez de connaissances de base relatives au common language runtime (CLR) et à la programmation orientée objet, ainsi que sur la conceptualisation des relations entre des éléments WPF sous la forme d'une arborescence. Pour pouvoir suivre les exemples de cette rubrique, vous devez également maîtriser Extensible Application Markup Language (XAML) et savoir écrire des applications ou des pages WPF de base. Pour plus d'informations, consultez Procédure pas à pas : mise en route de WPF et Vue d'ensemble du langage XAML (WPF).

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

Vous pouvez considérer les événements routés d'un point de vue fonctionnel ou d'implémentation. Cette rubrique présente les deux définitions, car certaines personnes comprennent mieux l'une que l'autre.

Définition fonctionnelle : Un événement routé est un type d'événement qui peut appeler des gestionnaires sur plusieurs écouteurs dans une arborescence d'éléments, plutôt que seulement sur l'objet qui a déclenché l'événement.

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

Une application WPF classique contient de nombreux éléments. Qu'ils soient créés à l'aide de code ou déclarés en XAML, ces éléments existent dans une relation d'arborescence d'éléments réciproque. L'itinéraire d'événement peut suivre l'une des deux directions en fonction de la définition d'événement. Toutefois, l'itinéraire part généralement de l'élément source puis "se propage" vers le haut dans l'arborescence d'éléments jusqu'à ce qu'il atteigne la racine de l'arborescence d'éléments (en général, une page ou une fenêtre). Ce concept de propagation peut vous être familier si vous avez déjà utilisé le modèle objet DHTML.

Soit l'arborescence d'éléments simple 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 se présente comme suit :

Boutons Oui, Non et Annuler

Dans cette arborescence d'éléments simplifiée, la source d'un événement Click est l'un des éléments Button et le Button sur lequel l'utilisateur a cliqué correspond au premier élément pouvant gérer l'événement. Toutefois, si aucun gestionnaire attaché au Button n'agit sur l'événement, celui-ci se propage vers le haut jusqu'au parent Button de l'arborescence d'éléments, à savoir le StackPanel. Potentiellement, l'événement se propage jusqu'à Border, puis au-delà pour atteindre la racine de page de l'arborescence d'éléments (non illustré).

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

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

Scénarios de niveau supérieur pour les événements routés

Vous trouverez ci-après un bref résumé des scénarios à l'origine du concept d'événement routé qui vous permettra de comprendre pourquoi un événement CLR classique n'est pas approprié pour ces scénarios :

Composition et encapsulation de contrôles : dans WPF, différents contrôles présentent un modèle de contenu riche. Par exemple, vous pouvez placer une image dans un Button, ce qui étend l'arborescence d'éléments visuels du bouton. Toutefois, l'image ajoutée ne doit pas interrompre le comportement de test d'atteinte qui entraîne la réponse d'un bouton à un Click de son contenu, même si l'utilisateur clique sur des pixels faisant techniquement partie de l'image.

Points d'attache de gestionnaire singuliers : dans Windows Forms, vous devez attacher plusieurs fois le même gestionnaire pour traiter les événements pouvant être déclenchés par 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 de gestionnaire pour déterminer l'origine de l'événement, si nécessaire. Par exemple, le gestionnaire ci-dessous peut correspondre au langage XAML précédent :

      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
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;
}

Gestion de classe : les événements routés autorisent un gestionnaire statique défini par la classe. Ce gestionnaire de classe peut gérer un événement avant les gestionnaires d'instance attachés.

Référencement d'un événement sans réflexion : pour certaines techniques de code et de balisage, il est nécessaire de pouvoir identifier un événement spécifique. Un événement routé crée un champ RoutedEvent en tant qu'identificateur afin de fournir une technique d'identification d'événement fiable qui ne nécessite pas de réflexion statique ou du runtime.

Implémentation des événements routés

Un événement routé est un événement CLR stocké par une instance de la classe RoutedEvent et inscrit auprès du système d'événements WPF. L'instance RoutedEvent obtenue lors de l'inscription est généralement conservée en tant que membre de champ public static readonly de la classe qui inscrit et qui "possède" l'événement routé. La connexion à l'événement CLR ayant un nom identique (parfois appelé événement "wrapper") est réalisée en substituant les implémentations add et remove pour l'événement CLR. Les implémentations add et remove sont normalement conservées comme valeurs par défaut implicites qui utilisent la syntaxe d'événement spécifique du langage appropriée pour ajouter et supprimer des gestionnaires de cet événement. Du point de vue du concept, le mécanisme de connexion et de stockage d'événements routés est semblable au principe d'une propriété de dépendance qui correspond à une propriété CLR stockée par la classe DependencyProperty et inscrite auprès du système de propriétés WPF.

L'exemple suivant affiche la déclaration d'un événement routé Tap personnalisé, y compris l'inscription et l'exposition du champ d'identificateur RoutedEvent et des implémentations add et remove pour l'événement TapCLR.

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
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); }
}

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 correspondant à un écouteur d'événements. La valeur de l'attribut est le nom de la méthode de gestionnaire implémentée, qui doit exister dans la classe partielle du fichier code-behind.

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

La syntaxe XAML utilisée pour ajouter des gestionnaires d'événements CLR standard permet également d'ajouter des gestionnaires d'événements routés, car vous ajoutez des gestionnaires au wrapper d'événement CLR, qui contient une implémentation d'événement routé sous-jacente. Pour plus d'informations sur l'ajout de gestionnaires d'événements en XAML, consultez Vue d'ensemble du langage XAML (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 sur la source d'événement sont appelés. L'événement routé passe ensuite par 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. En règle générale, les événements routés de propagation servent à signaler les modifications d'entrée ou d'état à partir de contrôles distincts ou d'autres éléments de l'interface utilisateur.

  • Routage direct : seul l'élément source peut appeler des gestionnaires en réponse. Ce principe est similaire au "routage" utilisé par Windows Forms pour les événements. Toutefois, contrairement à un événement CLR standard, les événements routés directs prennent en charge la gestion de classe (expliquée dans une section ultérieure) et peuvent être utilisés par EventSetter et EventTrigger.

  • Tunneling : initialement, des 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 sur l'itinéraire jusqu'à atteindre l'élément 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 sorte que les événements de parties composites peuvent être délibérément supprimés ou remplacés par des événements spécifiques du contrôle complet. Les événements d'entrée fournis dans WPF sont souvent implémentés sous la forme d'une parie tunneling/propagation. En outre, les événements de tunneling sont parfois appelés "événements d'aperçu" en raison d'une convention d'affectation de noms utilisée pour les paires.

Pourquoi utiliser des événements routés ?

En tant que développeur d'applications, vous n'avez pas toujours besoin de savoir si l'événement géré est implémenté en tant qu'événement routé. Les événements routés présentent un comportement particulier, qui est toutefois invisible en grande partie si vous gérez un événement sur l'élément où il est déclenché.

Les événements routés deviennent performants si vous utilisez l'un des scénarios suggérés : définition des gestionnaires communs au niveau d'une racine commune, composition d'un contrôle personnalisé ou définition d'une classe de contrôle personnalisée.

Les écouteurs et les sources d'événements routés ne doivent pas nécessairement partager un événement commun dans leur hiérarchie. Un UIElement ou un ContentElement peut être un écouteur d'événements pour un événement routé. Par conséquent, vous pouvez utiliser le jeu complet d'événements routés disponibles dans l'interface API active définie en tant qu'"interface" conceptuelle les différents éléments de l'application peuvent échanger des informations d'événement. Ce concept d'"interface" pour les événements routés convient tout particulièrement pour les événements d'entrée.

Les événements routés peuvent également être utilisés pour communiquer dans l'arborescence d'éléments, car les données de l'événement sont perpétuées à chaque élément de l'itinéraire. Un élément peut modifier les données d'événement, modification qui est ensuite disponible pour l'élément suivant de l'itinéraire.

Outre l'aspect de routage, deux autres raisons peuvent justifier l'implémentation d'un événement WPF donné en tant qu'événement routé au lieu d'un événement CLR standard. Si vous implémentez des événements personnalisés, vous pouvez également tenir compte des principes suivants :

  • Pour certaines fonctionnalités liées aux styles et aux modèles WPF (comme EventSetter et EventTrigger), l'événement référencé doit être un événement routé. Il s'agit du scénario d'identificateur de l'événement mentionné précédemment.

  • Les événements routés prennent en charge un mécanisme de gestion de classe qui permet à la classe de spécifier des méthodes statiques pouvant gérer les événements routés avant que les gestionnaires d'instance inscrits n'y accèdent. Cela s'avère particulièrement utile dans le cadre de la conception de contrôles, car la classe peut appliquer des comportements de classe pilotés par des évènements qui ne peuvent pas être supprimés par inadvertance lors de la gestion d'un événement sur une instance.

Chacune des considérations ci-dessus fait l'objet d'une section distincte 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 dans XAML, il vous suffit d'ajouter le nom de l'événement à un élément en tant qu'attribut et de définir 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 délégué RoutedEventHandler, qui est le délégué de gestionnaires d'événements de l'événement Click. Le premier paramètre de chaque délégué de gestionnaires d'événements routés spécifie l'élément auquel le gestionnaire d'événements est ajouté, tandis que le second paramètre détermine les données de l'événement.

      Private Sub b1SetColor(ByVal sender As Object, ByVal args As RoutedEventArgs)
        'logic to handle the Click event


...


      End Sub
void b1SetColor(object sender, RoutedEventArgs args)
{
  //logic to handle the Click event


...


}

RoutedEventHandler correspond au délégué de gestionnaires d'événements routés de base. En ce qui concerne les événements routés conçus spécialement pour certains contrôles ou scénarios, les délégués à utiliser pour les gestionnaires d'événements routés peuvent se spécialiser davantage et 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 événement routé DragEnter. Le gestionnaire doit implémenter le délégué DragEventHandler. Si vous utilisez le délégué le plus spécifique, vous pouvez traiter DragEventArgs dans le gestionnaire et lire la propriété Data, qui contient la charge utile de Presse-papiers de l'opération glisser.

Pour un exemple complet indiquant comment ajouter un gestionnaire d'événements à un élément à l'aide de XAML, consultez Comment : gérer un événement routé.

L'ajout d'un gestionnaire pour un événement routé dans une application créée à l'aide de code est une opération très simple. En effet, les gestionnaires d'événements routés peuvent toujours être ajoutés via une méthode d'assistance AddHandler (identique à celle appelée par le stockage pour add.) Toutefois, les événements routés WPF existants comportent généralement des implémentations de stockage de la logique add et remove qui permet d'ajouter les gestionnaires d'événements routés à l'aide d'une syntaxe d'événement spécifique d'un langage, plus intuitive que la méthode d'assistance. L'exemple suivant illustre une utilisation de la méthode d'assistance :

       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
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     
 }

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

        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
void MakeButton2()
{
  Button b2 = new Button();
  b2.Click += new RoutedEventHandler(Onb2Click2);
}
void Onb2Click2(object sender, RoutedEventArgs e)
{
  //logic to handle the Click event     
}

Pour un exemple indiquant comment ajouter un gestionnaire d'événements à l'aide de code, consultez Comment : ajouter un gestionnaire d'événements à l'aide de code.

Si vous utilisez Visual Basic, vous pouvez également utiliser le 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 Handled

Tous les événements routés partagent une classe de base de données d'événement commune, RoutedEventArgs. RoutedEventArgs définit la propriété Handled, qui accepte une valeur booléenne. Le but de la propriété Handled est de permettre à un gestionnaire d'événements situé sur l'itinéraire de marquer l'événement routé comme étant géré, en définissant Handled sur la valeur true. Une fois 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 situé sur l'itinéraire.

La valeur de Handled détermine la façon dont un événement routé est signalé ou traité lorsqu'il parcourt l'itinéraire. Si Handled a la valeur true dans les données d'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. Ce principe est valable pour les gestionnaires attachés en XAML et pour ceux ajoutés par des syntaxes d'attachement de gestionnaire d'événements spécifiques d'un langage telles que += et Handles. Pour la plupart des scénarios de gestionnaire communs, si un événement est marqué comme étant géré en définissant Handled sur la valeur true, le routage est « interrompu » pour un itinéraire de tunneling ou de propagation, ainsi que pour tous les événements gérés à un point de l'itinéraire par un gestionnaire de classe.

Toutefois, un mécanisme "handledEventsToo" permet aux écouteurs d'exécuter des gestionnaires en réponse à des événements routés où Handled a la valeur true dans les données d'événement. Autrement dit, l'itinéraire d'événement n'est pas réellement interrompu lors du marquage des données d'événement 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 d'un langage compatible avec les événements CLR généraux, appelez la méthode WPF AddHandler(RoutedEvent, Delegate, Boolean) pour ajouter votre gestionnaire. Affectez la valeur true à handledEventsToo.

  • Dans un EventSetter, définissez l'attribut HandledEventsToo sur la valeur true.

Outre le comportement induit par l'état Handled sur les événements routés, le concept de Handled a des conséquences sur la conception de l'application et sur l'écriture du code du gestionnaire d'événements. Vous pouvez conceptualiser Handled en tant que protocole simple exposé par les événements routés. L'utilisation de ce protocole varie en fonction de vos besoins. Toutefois, l'étude conceptuelle liée à l'utilisation de la valeur de Handled est la suivante :

  • Si un événement routé est marqué comme géré, il ne doit plus nécessairement être à nouveau géré par les autres éléments de cet itinéraire.

  • Si un événement routé n'est pas marqué comme géré, les autres écouteurs situés en amont sur l'itinéraire ont choisi de ne pas inscrire de gestionnaire ou les gestionnaires inscrits ont choisi de ne pas manipuler les données d'événement et de définir Handled sur la valeur true. (Il est évidemment possible que l'écouteur actuel corresponde au premier point de l'itinéraire.) Les gestionnaires sur l'écouteur actuel disposent de trois actions possibles :

    • Ne prendre aucune action. L'événement reste non géré et il passe à l'écouteur suivant.

    • Exécuter le code en réponse à l'événement, tout en déterminant que l'action prise n'est pas assez importante pour garantir le marquage de l'événement comme géré. L'événement passe à l'écouteur suivant.

    • Exécuter le 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 prise est jugée suffisamment importante pour garantir le marquage comme géré. L'événement passe encore à l'écouteur suivant, mais ses données d'événement comportent Handled=true. Ainsi, seuls les écouteurs handledEventsToo peuvent appeler d'autres gestionnaires.

Cette étude conceptuelle est renforcée par le comportement de routage mentionné précédemment. Il est dès lors plus difficile (mais toutefois possible dans le code ou des styles) d'attacher des gestionnaires pour les événements routés appelés même si un gestionnaire précédent de l'itinéraire a déjà défini Handled sur la valeur true.

Pour plus d'informations sur Handled, sur la gestion de classe d'événements routés et sur les scénarios dans lesquels il est recommandé de marquer un événement routé comme Handled, consultez Marquage des événements routés comme gérés et gestion de classe.

Dans les applications, il est fréquent de gérer uniquement un événement routé de propagation sur l'objet qui l'a déclenché, sans se soucier des caractéristiques de routage de l'événement. Toutefois, il est toujours recommandé de marquer un événement routé comme géré dans les données d'événement afin d'éviter tout effet secondaire indésirable si un élément situé à un niveau supérieur de l'arborescence d'éléments comporte également un gestionnaire attaché pour cet événement routé.

Gestionnaires de classe

Si vous définissez une classe dérivée de DependencyObject, vous pouvez également définir et attacher un gestionnaire de classe pour un événement routé correspondant à 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 attachés à une instance de cette classe, lorsqu'un événement routé atteint une instance d'élément sur son itinéraire.

Certains contrôles WPF bénéficient de la gestion de classe inhérente pour certains événements routés. De l'extérieur, on peut penser que l'événement routé n'est jamais déclenché, alors qu'en réalité, il est géré par la classe. L'événement routé peut potentiellement encore être géré par vos gestionnaires d'instance si vous utilisez certaines techniques. Par ailleurs, de nombreuses 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 le contournement d'une gestion de classe indésirable 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 d'événement spécial appelé événement attaché. Grâce à un événement attaché, vous pouvez ajouter un gestionnaire pour un événement particulier à un élément arbitraire. L'élément qui gère l'événement ne doit pas définir l'événement attaché ou en hériter. 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 en tant que membre de classe.

Le système d'entrée WPF utilise largement les événements attachés. Toutefois, presque tous ces événements attachés sont transférés via des éléments de base. Les événements d'entrée apparaissent ensuite en tant qu'événements routés non attachés équivalents qui font partie de la classe d'élément de base. Par exemple, l'événement attaché Mouse.MouseDown sous-jacent peut être plus facilement géré sur un UIElement donné en utilisant MouseDown sur ce UIElement plutôt qu'en se servant de la syntaxe d'événement attaché en XAML ou dans le code.

Pour plus d'informations sur les événements attachés dans WPF, consultez Vue d'ensemble des événements attachés.

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

L'attachement de gestionnaires pour les événements routés déclenchés par les éléments enfants constitue une autre utilisation de syntaxe qui ressemble à la syntaxe d'événement attaché NomType.NomÉvénement. Il ne s'agit toutefois pas à proprement parler d'une utilisation d'événement attaché. Pour tirer parti du routage d'événement, vous attachez les gestionnaires à un parent commun, même si l'événement routé approprié n'est pas membre de ce parent commun. Reprenons l'exemple suivant :

<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>

Dans ce cas, l'écouteur d'élément parent auquel 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 classe Button (ButtonBase en réalité, mais disponible dans Button par héritage). Button "possède" l'événement, mais le système d'événements routés autorise l'attachement des gestionnaires d'un événement routé à un écouteur d'instance UIElement ou ContentElement, qui peuvent, dans d'autres cas, attacher des écouteurs pour un événement common language runtime (CLR). L'espace de noms xmlns par défaut pour ces noms d'attributs d'événement qualifiés correspond en général à l'espace de noms xmlns WPF par défaut. Il est toutefois également possible de spécifier des espaces de noms préfixés pour des é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

Les événements d'entrée constituent une application fréquente des événements routés dans la plateforme WPF. Dans WPF, les noms d'événements routés de tunneling sont, par convention, précédés du mot « Preview ». Les événements d'entrée se présentent souvent sous la forme de paires, l'un correspondant à l'événement de propagation et l'autre à l'événement de tunneling. Par exemple, les événements KeyDown et PreviewKeyDown comportent la même signature, le premier étant l'événement d'entrée de propagation et le dernier, l'événement d'entrée de tunneling. Parfois, les événements d'entrée ne comportent qu'une version de propagation ou 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 des stratégies de routage différentes si ces événements routés existent. En outre, les sections des pages de références managées clarifient la stratégie de routage de chaque événement routé.

Les événements d'entrée WPF se présentant sous la forme de paires sont implémentés afin qu'une seule action d'entrée utilisateur (clic sur un bouton de la souris, par exemple) déclenche les deux événements de la paire dans l'ordre. Tout d'abord, l'événement de tunneling est déclenché et parcourt son itinéraire. Ensuite, c'est l'événement de propagation qui est déclenché et qui parcourt son itinéraire. Ces deux événements partagent littéralement la même instance de données d'événement, car l'appel de méthode RaiseEvent dans la classe d'implémentation qui déclenche l'événement de propagation écoute les données d'événement issues de l'événement de tunneling et les réutilise dans le nouvel événement déclenché. Les écouteurs comportant des gestionnaires pour l'événement de tunneling sont les premiers à pouvoir marquer l'événement routé comme géré (gestionnaires de classe d'abord, puis gestionnaires d'instance). Si un élément situé sur l'itinéraire de tunneling a marqué l'événement routé comme géré, les données de l'événement déjà géré sont envoyées pour l'événement de propagation et aucun gestionnaire standard attaché pour les événements d'entrée de propagation équivalents n'est appelé. D'un point de vue externe, l'événement de propagation géré se présente comme s'il n'avait pas été déclenché. Ce comportement de gestion s'avère utile pour composer des contrôles. En effet, vous pouvez déterminer que tous les événements d'entrée basés sur un test d'atteinte ou sur un focus doivent être signalés par le dernier contrôle, plutôt que par ses parties composites. Étant plus proche de la racine dans la composition, le dernier élément de contrôle peut gérer d'abord la classe pour l'événement de tunneling, puis "remplacer" cet événement routé par un événement plus spécifique du contrôle, dans le cadre du code qui stocke la classe de contrôle.

Afin d'illustrer le fonctionnement du traitement des événements d'entrée, prenons l'exemple d'événement d'entrée suivant. Dans cette illustration d'arborescence, leaf element #2 correspond à la source d'un événement PreviewMouseDown, puis d'un événement MouseDown.

Propagation et tunneling d'événements d'entrée

Diagramme de routage d'événements

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 n°1.

  3. PreviewMouseDown (tunneling) sur l'élément source n°2.

  4. MouseDown (propagation) sur l'élément source n°2.

  5. MouseDown (propagation) sur l'élément intermédiaire n°1.

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

Un délégué de gestionnaires d'événements routés fournit les références à deux objets : l'objet qui a déclenché l'événement et celui où le gestionnaire a été appelé. Ce dernier correspond à l'objet signalé par le paramètre sender, tandis que l'objet dans lequel l'événement a été déclenché pour la première fois est signalé par la propriété Source dans les données d'événement. Un événement routé peut encore être déclenché et géré par le même objet, auquel cas sender et Source sont identiques (c'est le cas pour les étapes 3 et 4 de la liste de l'exemple de traitement d'événements).

En raison du tunneling et de la propagation, les éléments parents reçoivent des événements d'entrée là où Source correspond à un élément enfant. Si l'élément source doit être connu, vous pouvez l'identifier en accédant à la propriété Source.

En règle générale, une fois l'événement d'entrée marqué comme Handled, aucun autre gestionnaire n'est appelé par la suite. Vous devez marquer des événements d'entrée comme gérés dès qu'un gestionnaire est appelé pour traiter la gestion logique spécifique de l'application afin de déterminer la signification de l'événement d'entrée.

Ce principe général relatif à l'état Handled présente toutefois une exception : les gestionnaires d'événements d'entrée inscrits pour ignorer délibérément l'état Handled pour les données d'événement peuvent encore être appelés sur l'un des itinéraires. Pour plus d'informations, consultez Aperçu des événements ou Marquage des événements routés comme gérés et gestion de classe.

Le modèle des données d'événement partagées entre les événements de tunneling et de propagation, ainsi que le déclenchement séquentiel des événements de tunneling, puis de propagation, constituent un concept qui n'est généralement pas valable pour l'ensemble des événements routés. Ce comportement est implémenté par la façon dont les périphériques d'entrée WPF déclenchent les paires d'événements d'entrée et s'y connectent. L'implémentation d'événements d'entrée personnalisés est un scénario avancé, mais vous pouvez également suivre ce modèle dans ce contexte.

Certaines classes gèrent par classe des événements d'entrée spécifiques, généralement pour redéfinir la signification d'un événement d'entrée donné piloté par l'utilisateur dans ce contrôle et pour 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 l'entrée et sur l'interaction entre l'entrée et 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 d'événements XAML déclarés au préalable dans le balisage à l'aide d'un EventSetter. Lors de l'application du style, le gestionnaire référencé est ajouté à l'instance à laquelle est appliquée le style. Vous ne pouvez déclarer un EventSetter que pour un événement routé (voir l'exemple ci-dessous). Notez que la méthode b1SetColor référencée dans cet exemple est située dans un fichier code-behind.

<StackPanel
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://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>

Cet exemple présente l'avantage que le style peut contenir une quantité importante d'autres informations pouvant s'appliquer aux boutons de votre application. En outre, le EventSetter faisant partie de ce style favorise la réutilisation de code au niveau du balisage. Par ailleurs, un EventSetter isole davantage les noms de méthodes pour les gestionnaires par rapport à l'application générale et au balisage de page.

EventTrigger représente une autre syntaxe spéciale qui combine les fonctionnalités d'événement routé et d'animation de WPF. Tout comme avec EventSetter, seuls les événements routés peuvent être utilisés pour un EventTrigger. En général, un EventTrigger est déclaré dans le cadre d'un style, mais un EventTrigger peut également être déclaré sur des éléments de page dans le cadre de la collection Triggers ou bien dans un ControlTemplate. Un EventTrigger vous permet de spécifier un Storyboard s'exécutant à chaque fois qu'un événement routé atteint un élément sur son itinéraire déclarant un EventTrigger pour cet événement. Par rapport à la simple gestion de l'événement et au démarrage d'un storyboard existant, un EventTrigger présente l'avantage de fournir un meilleur contrôle sur le storyboard et sur son comportement au moment de l'exécution. Pour plus d'informations, consultez Comment : 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 décrit principalement les événements routés en termes de concepts de base et d'aide sur la manière de répondre aux événements routés déjà présents dans les différents contrôles et éléments de base. Vous pouvez toutefois créer un événement routé personnalisé sur une classe personnalisée avec la prise en charge nécessaire, comme des classes de données d'événement et des délégués spéciaux. Le propriétaire d'un événement routé peut être une classe, mais, pour pouvoir être utiles, les événements routés doivent être déclenchés et gérés par des classes dérivées UIElement ou ContentElement. Pour plus d'informations sur les événements personnalisés, consultez Comment : créer un événement routé personnalisé.

Voir aussi

Référence

EventManager

RoutedEvent

RoutedEventArgs

Concepts

Marquage des événements routés comme gérés et gestion de classe

Vue d'ensemble des entrées

Vue d'ensemble des commandes

Propriétés de dépendance personnalisées

Arborescences dans WPF

Modèles d'événement faible