Accueil > WPF > [WPF] : Custom RoutedEvents Tunnel-Bubble-Direct

[WPF] : Custom RoutedEvents Tunnel-Bubble-Direct

Plusieurs personnes m’ont déjà contacté au sujet de soucis d’utilisation / compréhension des RoutedEvents présents dans WPF.

Voici un exemple complet d’utilisation simple des RoutedEvent Bubble et Tunnel !

Présentation

WPF à introduit un nouvelle notion au sein du mécanisme standard de gestion des évènements que l’on peut connaitre dans le framework .net, ce sont des évènements « intelligents » nommés RoutedEvent.
Pour que vous puissiez utiliser cette notion vous devez disposer d’un controle héritant au minimum de UIElement.

Les différents types de RoutedEvent

Il existe 3 types de RoutedEvents :

  • Bubble : L’évènement est déclenché sur la source et est ensuite déclenché à tous ses parents en remontant jusqu’à ce qu’il arrive à la racine.
  • Tunnel : C’est l’inverse de Bubble, dans ce cas l’évènement est déclanché depuis l’élément racine et parcours tous ses fils jusqu’à ce qu’il rencontre l’élément à l’origine de l’évènement.
  • Direct : Signifie que seul l’élément lui même peut déclencher l’évènement. C’est la notion d’évènement classique que l’on peut connaitre dans .net ou les évènements WinForm pour ce qui est des composants utilisateur.

Voici un graphique illustrant le fonctionnement des RoutedEvent Bubble et Tunnel :

WPF-RoutedEvent-Bubble-Tunnel

Comment définir un RoutedEvent Bubble ?

Rien de plus simple, à l’instar des DependencyProperty il suffit de déclarer l’évènement de la manière suivante :

public static readonly RoutedEvent FOOEvent = EventManager.RegisterRoutedEvent("FOO", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MonCustomControl));

Ensuite vous devez écrire le Wrapper .net

public event RoutedEventHandler FOO
{
add
{
AddHandler(FOOEvent, value);
}
remove
{
RemoveHandler(FOOEvent, value);
}
}

Comment définir un RoutedEvent Tunnel ?

C’est exactement la même chose sauf qu’il faut mettre Tunnel et ne pas oublier de préfixer les évènement par « Preview » afin de respecter les règles de nommages instaurées dans le framework.

public static readonly RoutedEvent PreviewFOOEvent = EventManager.RegisterRoutedEvent("PreviewFOO", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(MonCustomControl));

Ensuite vous devez écrire le Wrapper .net en n’oubliant pas « Preview »

public event RoutedEventHandler PreviewFOO
{
add
{
AddHandler(PreviewFOOEvent, value);
}
remove
{
RemoveHandler(PreviewFOOEvent, value);
}
}

Comment définir un RoutedEvent Direct?

Il faut faire comme Bubble mais mettre Direct pour la stratégie de routage.

public static readonly RoutedEvent FOOEvent = EventManager.RegisterRoutedEvent("FOO", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(MonCustomControl));

Comment déclencher un RoutedEvent ?

Il suffit d’appeler la séquence de code suivante

RaiseEvent(new RoutedEventArgs(MonCustomControl.FOOEvent));

Exemple complet d’un CustomControl avec un RoutedEvent Bubble

 public class CustomButton : Button
    {
        public static readonly RoutedEvent HelloWorldEvent = EventManager.RegisterRoutedEvent("HelloWorld", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(CustomButton));

        public event RoutedEventHandler HelloWorld
        {
            add
            {
                AddHandler(HelloWorldEvent, value);
            }
            remove
            {
                RemoveHandler(HelloWorldEvent, value);
            }
        }

        protected override void OnClick()
        {
            RaiseEvent(new RoutedEventArgs(CustomButton.HelloWorldEvent));
        }
    }

Exemple complet d’un CustomControl avec un RoutedEvent Tunnel

 public class CustomButton : Button
    {
        public static readonly RoutedEvent PreviewHelloWorldEvent = EventManager.RegisterRoutedEvent("PreviewHelloWorld", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(CustomButton));

        public event RoutedEventHandler PreviewHelloWorld
        {
            add
            {
                AddHandler(PreviewHelloWorldEvent, value);
            }
            remove
            {
                RemoveHandler(PreviewHelloWorldEvent, value);
            }
        }

        protected override void OnClick()
        {
            RaiseEvent(new RoutedEventArgs(CustomButton.PreviewHelloWorldEvent));
        }
    }

Comment utiliser les RoutedEvent en XAML ?

Il suffit de déclarer les évènements en tant que propriétés attachées.

<Grid x:Name="root" local:CustomButton.HelloWorld="HelloWorldEventHandler"></Grid>

Exemple complet permettant de voir le fonctionnement du mode Bubble

XAML

<Window x:Class="RoutedEventSample.MainWindow"
		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		Title="MainWindow"
		xmlns:local="clr-namespace:RoutedEventSample">

	<Grid x:Name="root"
		  local:CustomButton.HelloWorld="EventCatchedHandler">
		<Grid.RowDefinitions>
			<RowDefinition Height="450" />
			<RowDefinition />
		</Grid.RowDefinitions>
		<Grid x:Name="GrilleJaune"
			  local:CustomButton.HelloWorld="EventCatchedHandler"
			  Background="#FFFFF36B">
			<Grid.ColumnDefinitions>
				<ColumnDefinition />
				<ColumnDefinition />
			</Grid.ColumnDefinitions>

			<Border Background="#FF91FF6B"
					local:CustomButton.HelloWorld="EventCatchedHandler"
					x:Name="BorderVert"
					Margin="40,40,20,40">
				<Border Background="#FFFD7171"
						Margin="40"
						x:Name="BorderRouge"
						local:CustomButton.HelloWorld="EventCatchedHandler">
					<local:CustomButton x:Name="BoutonGauche"
										Content="Button"
										HorizontalAlignment="Center"
										VerticalAlignment="Center"
										local:CustomButton.HelloWorld="EventCatchedHandler" />
				</Border>
			</Border>

			<Border Background="#FF92A7FF"
					Grid.Column="1"
					x:Name="BorderBleu"
					local:CustomButton.HelloWorld="EventCatchedHandler"
					Margin="20,40,40,40">
				<Border Background="#FFBC71FD"
						Margin="40"
						Grid.Column="1"
						local:CustomButton.HelloWorld="EventCatchedHandler"
						x:Name="BorderViolet">
					<local:CustomButton x:Name="BoutonDroite"
										Content="Button"
										HorizontalAlignment="Center"
										VerticalAlignment="Center"
										local:CustomButton.HelloWorld="EventCatchedHandler" />
				</Border>
			</Border>

		</Grid>

		<GridSplitter Grid.Row="1"
					  HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="5" Panel.ZIndex="1"/>

		<ListBox x:Name="EventList"
				 Grid.Row="1"
				 Grid.ColumnSpan="2" />
		<Button Content="Effacer"
				Click="Button_Click"
				HorizontalAlignment="Right"
				VerticalAlignment="Top"
				Margin="0,5,5,0" Grid.Row="1"/>

	</Grid>
</Window>

CS

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void EventCatchedHandler(object sender, RoutedEventArgs e)
        {
            EventList.Items.Add(String.Format("{0:T} - Evenement {1} reçu sur {2}", DateTime.Now, e.RoutedEvent.Name, ((FrameworkElement)sender).Name));
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            EventList.Items.Clear();
        }
    }

Aperçu

On s’aperçoit bien que les évènements remontent du bouton jusqu’à la racine 🙂

RoutedEventSample

Exemple complet permettant de voir le fonctionnement du mode Tunnel

XAML

<Window x:Class="RoutedEventSample.MainWindow"
		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		Title="MainWindow"
		xmlns:local="clr-namespace:RoutedEventSample">

	<Grid x:Name="root"
		  local:CustomButton.PreviewHelloWorld="EventCatchedHandler">
		<Grid.RowDefinitions>
			<RowDefinition Height="450" />
			<RowDefinition />
		</Grid.RowDefinitions>
		<Grid x:Name="GrilleJaune"
			  local:CustomButton.PreviewHelloWorld="EventCatchedHandler"
			  Background="#FFFFF36B">
			<Grid.ColumnDefinitions>
				<ColumnDefinition />
				<ColumnDefinition />
			</Grid.ColumnDefinitions>

			<Border Background="#FF91FF6B"
					local:CustomButton.PreviewHelloWorld="EventCatchedHandler"
					x:Name="BorderVert"
					Margin="40,40,20,40">
				<Border Background="#FFFD7171"
						Margin="40"
						x:Name="BorderRouge"
						local:CustomButton.PreviewHelloWorld="EventCatchedHandler">
					<local:CustomButton x:Name="BoutonGauche"
										Content="Button"
										HorizontalAlignment="Center"
										VerticalAlignment="Center"
										local:CustomButton.PreviewHelloWorld="EventCatchedHandler" />
				</Border>
			</Border>

			<Border Background="#FF92A7FF"
					Grid.Column="1"
					x:Name="BorderBleu"
					local:CustomButton.PreviewHelloWorld="EventCatchedHandler"
					Margin="20,40,40,40">
				<Border Background="#FFBC71FD"
						Margin="40"
						Grid.Column="1"
						local:CustomButton.PreviewHelloWorld="EventCatchedHandler"
						x:Name="BorderViolet">
					<local:CustomButton x:Name="BoutonDroite"
										Content="Button"
										HorizontalAlignment="Center"
										VerticalAlignment="Center"
										local:CustomButton.PreviewHelloWorld="EventCatchedHandler" />
				</Border>
			</Border>

		</Grid>

		<GridSplitter Grid.Row="1"
					  HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="5" Panel.ZIndex="1"/>

		<ListBox x:Name="EventList"
				 Grid.Row="1"
				 Grid.ColumnSpan="2" />
		<Button Content="Effacer"
				Click="Button_Click"
				HorizontalAlignment="Right"
				VerticalAlignment="Top"
				Margin="0,5,5,0" Grid.Row="1"/>

	</Grid>
</Window>

CS

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void EventCatchedHandler(object sender, RoutedEventArgs e)
        {
            EventList.Items.Add(String.Format("{0:T} - Evenement {1} reçu sur {2}", DateTime.Now, e.RoutedEvent.Name, ((FrameworkElement)sender).Name));
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            EventList.Items.Clear();
        }
    }

Aperçu

On s’aperçoit bien que les évènements sont déclanchés de la racine vers le bouton ! 🙂

RoutedEventSampleTunnel

Avec ces exemples tout devrait être beaucoup plus clair à présent 😀

Publicités
Catégories :WPF
  1. Aucun commentaire pour l’instant.
  1. No trackbacks yet.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s

%d blogueurs aiment cette page :