بررسی Routed Event ها در WPF
در WPF رویداد Routed Event نوعی event است که می تواند علاوه بر شیء که آن را rise کرده، چندین هندلر را در listener های مختلف Invoke کنید. Routed Event ها سه نوع استراتژی اصلی مسیریابی دارند که در زیر مشاهده میکنید.
- Direct Event
- Bubbling Event
- Tunnel Event
Direct Event
این نوع رویداد شبیه به رویدادهای Windows Forms است که توسط عنصری که صاحب آن رویداد است، Rise می شود. برخلاف یک رویداد استاندارد CLR، Direct Event ها قابلیت پشتیبانی از کلاس را دارند و همچین می توانند در Event Setter ها و یا Event Trigger ها استفاده شوند. برای مثال رویداد MouseEnter یک رویداد از نوع Direct Event است.
Bubbling Event
این نوع رویداد با عنصری که صاحب آن است آغاز می شود و سپس به بالاترین عنصر موجود در Visual Tree می رسد. در WPF به احتمال زیاد بالاتری عنصر در Visual Tree یک Window است.
Tunnel Event
این نوع رویداد در ریشه درخت عناصر invoke می شود و سپس در Visual Tree به سمت پایین حرکت می کند (یعنی عناصر فرزند را پیمایش می کند) و تا زمانی که به عنصر صاحب آن رویداد برسد.
تفاوت بین bubbling event و tunneling event در این است که tunneling event ها همیشه با یک Preview در ابتدای نام آن ها شروع می شوند. در WPF اکثر رویداد ها به صورت جفت tunneling/bubbling ایجاد شده اند. برای مثال در WPF هم رویداد PreviewMouseDown وجود دارد و هم رویداد MouseDown.
برای درک بهتر نحوه عملکرد Routed Event ها به مثال زیر توجه کنید که در آن سه عدد TextBlock به همراه یک Button قرار داده شده است.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <Window x:Class="WpfPlayground.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Title="SourceSara.Com" Height="400" Width="600" Background="#202C39" TextBlock.Foreground="#fafafa" TextElement.Foreground="#fafafa" TextElement.FontSize="16" TextElement.FontFamily="Roboto" WindowStartupLocation="CenterScreen" ButtonBase.Click="OnWindowClicked"> <Grid HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel ButtonBase.Click="OnStackPanelClicked"> <TextBlock x:Name="ButtonTextBlock" Text="Button TextBlock" Margin="8"/> <TextBlock x:Name="StackPanelTextBlock" Text="StackPanel TextBlock" Margin="8"/> <TextBlock x:Name="WindowTextBlock" Text="Window TextBlock" Margin="8"/> <Button Content="Click me" Margin="8" Padding="8,6" HorizontalAlignment="Center" Click="OnButtonClicked"/> </StackPanel> </Grid> </Window> |
کد سی شارپ مربوط به مثال:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System.Windows; namespace WpfPlayground { public partial class MainWindow { public MainWindow() => InitializeComponent(); private void OnWindowClicked(object sender, RoutedEventArgs e) { WindowTextBlock.Text = "Click event is bubbled to Window"; } private void OnStackPanelClicked(object sender, RoutedEventArgs e) { StackPanelTextBlock.Text = "Click event is bubbled to Stack Panel"; } private void OnButtonClicked(object sender, RoutedEventArgs e) { ButtonTextBlock.Text = "Button is Clicked"; } } } |
زمانی که کد فوق را کامپایل و اجرا کنید خروجی زیر را مشاهده خواهید کرد.
و زمانی که بر روی دکمه کلیک کنید، خروجی برنامه به شکل زیر تغییر می کند.
اگر می خواهید از انتقال یک Routed Event جلوگیری کنید، کافیست از دستور e.Handled = true; استفاده کنید. برای مثال اگر بخواهیم جلوی انتقال رویداد را در StackPanel بگیریم، میتوانیم کد مربوط به آن را به شکل زیر تغییر دهیم.
1 2 3 4 5 | private void OnStackPanelClicked(object sender, RoutedEventArgs e) { StackPanelTextBlock.Text = "Click event is bubbled to Stack Panel"; e.Handled = true; } |
حال اگر برنامه را دوباره اجرا و بر روی دکمه کلیک کنید، خروجی زیر را مشاهده خواهید کرد.
همانطور که مشاهده می کنید، متن مربوط به Window تغییر نکرده و این به معنی فراخوانی نشدن هندلر مربوط به Window است.
Routed Event های سفارشی
در WPF و .NET framework شما می توانید Routed Event های سفارشی مورد نیاز خود را ایجاد کنید. برای ایجاد Routed Event سفارشی باید مراحل زیر را انجام دهید.
- ثبت رویداد سفارشی با استفاده از RegisterRoutedEvent
- مشخص کردن استراتژی رویداد سفارشی (Bubble، Tunnel و یا Direct)
- ایجاد یک هندلر برای رویداد سفارشی
برای درک بهتر نحوه ایجاد Routed Event سفارشی یک مثال ساده ایجاد می کنیم.
یک پروژه جدید از نوع WPF و با نام WPFCustomRoutedEvent ایجاد کنید سپس یک آیتم جدید از نوع Custom Control (WPF) به پروژه اضافه کرده و نام آن را MyCustomControl بگذارید.
بعد از انجام مراحل بالا دو فایل ThemesGeneric.xaml و MyCustomControl.cs به پروژه اضافه می شوند. حال فایل Generic.xaml را مانند نمونه زیر تغییر دهید تا ظاهر کنترل سفارشی ما ایجاد شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfPlayground"> <Style TargetType="{x:Type local:MyCustomControl}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:MyCustomControl}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Button x:Name= "PART_Button" Content="Click Me"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary> |
بعد از آن کد مربوط به فایل MyCustomControl را مانند نمونه زیر تغییر دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | using System.Windows; using System.Windows.Controls; namespace WpfPlayground { public class MyCustomControl : Control { static MyCustomControl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); //demo purpose only, check for previous instances and remove the handler first var button = GetTemplateChild("PART_Button") as Button; if (button != null) button.Click += Button_Click; } void Button_Click(object sender, RoutedEventArgs e) { RaiseClickEvent(); } public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyCustomControl)); public event RoutedEventHandler Click { add { AddHandler(ClickEvent, value); } remove { RemoveHandler(ClickEvent, value); } } protected virtual void RaiseClickEvent() { RoutedEventArgs args = new RoutedEventArgs(ClickEvent); RaiseEvent(args); } } } |
سپس ظاهر پنجره اصلی را مانند نمونه زیر تغییر دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <Window x:Class="WpfPlayground.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfPlayground" mc:Ignorable="d" Title="SourceSara.Com" Height="400" Width="600" Background="#202C39" TextBlock.Foreground="#fafafa" TextElement.Foreground="#fafafa" TextElement.FontSize="16" TextElement.FontFamily="Roboto" WindowStartupLocation="CenterScreen"> <Grid> <local:MyCustomControl Margin="64" Click="OnMyCustomControlClicked" /> </Grid> </Window> |
و در نهایت یک هندلر برای کنترل و رویداد سفارشی ایجاد کنید.
1 2 3 4 5 6 7 8 9 10 11 12 | using System.Windows; namespace WpfPlayground { public partial class MainWindow { public MainWindow() => InitializeComponent(); private void OnMyCustomControlClicked(object sender, RoutedEventArgs e) { MessageBox.Show("It is the custom routed event of your custom control"); } } } |
اگر کد فوق را کامپایل و اجرا کنید، خروجی زیر را مشاهده خواهید کرد.
با کلیک بر روی کنترل سفارشی پیام زیر نمایش داده می شود که نشان دهنده کار کردن رویداد سفارشی است که ایجاد کردیم.
هیچ نظری ثبت نشده است