Archive

Archive for septembre 2011

[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

[SL5] : Sortie de Silverlight 5 RC

Vous pouvez dès à présent télécharger la version RC de Silverlight 5 !

http://www.silverlight.net/learn/overview/what%27s-new-in-silverlight-5

Voici les liens utiles pour utiliser cette RC:

A programme les nouveautés suivantes :

  •      Support du P/Invoke pour appeler des méthodes natives
  •      Le support du 64 bits !
  •      Impression post script
  •      Contrôle à distance dans les médias
  •      Trusted App dans le navigateur possible et non plus uniquement en OOB
  •      PivotViewer inclut dans le plugin

Attention toutefois ceci n’est pas une version GoLive et il est donc possible que des changements soient réalisés d’ici à la sortie définitive. Ne l’utilisez pas dans un environnement de production, mais uniquement dans le but de vous faire la main et de tester d’éventuelles migrations.

Bon tests 🙂

Catégories :Silverlight, Silverlight 5