WPF之路由事件

自定义路由事件

  • 定义:在自定义控件中JSCNumericBox定义一个路由事件
        #region 路由事件
        // 1.定义路由事件
        public static readonly RoutedEvent MyCustomEvent = EventManager.RegisterRoutedEvent(
            "MyCustom",//事件名称
            RoutingStrategy.Bubble,//事件类型
            typeof(RoutedEventHandler),// 事件处理程序类型
            typeof(MainWindow)// 事件源类型
            );
        // 2.定义CLR事件包装器
        public event RoutedEventHandler MyCustom
        {
            add { AddHandler(MyCustomEvent, value); }
            remove { RemoveHandler(MyCustomEvent, value); }
        }
        // 3.触发路由事件的方法
        protected void OnMyCustom()
        {
            // 创建事件参数(如果需要)
            RoutedEventArgs args = new RoutedEventArgs(MyCustomEvent);
            // 触发事件
            RaiseEvent(args);
        }
        #endregion
  • 使用:在窗体中使用自定义控件的路由事件
        <local:JSCNumericBox Width="200" Height="50" MyCustom="JSCNumericBox_MyCustom"/>
        private void JSCNumericBox_MyCustom(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("路由事件触发");
            e.Handled = true;// 阻止事件传递
        }

这样你可以在JSCNumericBox里面任意地方编写逻辑调用OnMyCustom()来触发路由了。以下是完整示例:

public class JSCNumericBox : Control
{
    #region 路由事件
    // 1.定义路由事件
    public static readonly RoutedEvent MyCustomEvent = EventManager.RegisterRoutedEvent(
        "MyCustom",//事件名称
        RoutingStrategy.Bubble,//事件类型
        typeof(RoutedEventHandler),// 事件处理程序类型
        typeof(MainWindow)// 事件源类型
        );
    // 2.定义CLR事件包装器
    public event RoutedEventHandler MyCustom
    {
        add { AddHandler(MyCustomEvent, value); }
        remove { RemoveHandler(MyCustomEvent, value); }
    }
    // 3.触发路由事件的方法
    protected void OnMyCustom()
    {
        // 创建事件参数(如果需要)
        RoutedEventArgs args = new RoutedEventArgs(MyCustomEvent);
        // 触发事件
        RaiseEvent(args);
    }
    #endregion

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);
        OnMyCustom();// 调用路由事件
    }

    static JSCNumericBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(JSCNumericBox), new FrameworkPropertyMetadata(typeof(JSCNumericBox)));
    }
}
<Window x:Class="WPFLearn.Other.RoutedEvents.RoutedEventsWin"
        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:WPFLearn.Other.RoutedEvents"
        mc:Ignorable="d"
        Title="RoutedEventsWin" Height="450" Width="800">
    <Grid>
        <local:JSCNumericBox Width="200" Height="50" MyCustom="JSCNumericBox_MyCustom"/>
    </Grid>
</Window>
    public partial class RoutedEventsWin : Window
    {
        public RoutedEventsWin()
        {
            InitializeComponent();
        }

        private void JSCNumericBox_MyCustom(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("路由事件触发");
            e.Handled = true;// 阻止事件传递
        }
    }

路由事件的类型

  • 冒泡事件(Bubble):
    事件从最具体的元素(触发事件的元素)开始,向上冒泡到其父元素,直到到达根元素。
    例如,当用户点击一个按钮时,这个点击事件会首先在按钮上触发,然后逐级向上传递到按钮的父容器。
<Window x:Class="WPFLearn.Other.RoutedEvents.RoutedEventsWin"
        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:WPFLearn.Other.RoutedEvents"
        mc:Ignorable="d"
        Title="RoutedEventsWin" Height="450" Width="800" Button.Click="btn1_Click">
    <Grid>
        <!--冒泡事件测试-->
        <Grid x:Name="gdOuter" Button.Click="btn1_Click">
            <StackPanel x:Name="sp1" Button.Click="btn1_Click">
                <Grid x:Name="gd1" Button.Click="btn1_Click">
                    <Button x:Name="btn1" Content="点我" Click="btn1_Click" Margin="5" Padding="5" FontSize="18"></Button>
                </Grid>
                <RichTextBox x:Name="txtInfo" Margin="5" MinHeight="100"  ></RichTextBox>
            </StackPanel>
        </Grid>
    </Grid>
</Window>
    public partial class RoutedEventsWin : Window
    {
        public RoutedEventsWin()
        {
            InitializeComponent();
        }

        private void btn1_Click(object sender, RoutedEventArgs e)
        {
            this.txtInfo.AppendText(string.Format("当前响应事件对象:{0},响应事件原始对象:{1}\r\n", (sender as FrameworkElement).Name, e.OriginalSource));
        }
    }
image.png
  • 隧道事件(Tunnel):
    事件从根元素开始,向下传递到最具体的元素。这种方式与冒泡相反。
    Tunneling 事件的名称通常以 “Preview” 开头,例如 PreviewMouseDown,表示这是一个鼠标按下的隧道事件。
<Window x:Class="WPFLearn.Other.RoutedEvents.RoutedEventsWin"
        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:WPFLearn.Other.RoutedEvents"
        mc:Ignorable="d"
        Title="RoutedEventsWin" Height="450" Width="800" Button.PreviewMouseDown="btn1_PreviewMouseDown">
    <Grid>
        <!--隧道事件测试-->
        <Grid x:Name="gdOuter" Button.PreviewMouseDown="btn1_PreviewMouseDown">
            <StackPanel x:Name="sp1" Button.PreviewMouseDown="btn1_PreviewMouseDown">
                <Grid x:Name="gd1" Button.PreviewMouseDown="btn1_PreviewMouseDown">
                    <Button x:Name="btn1" Content="点我" PreviewMouseDown="btn1_PreviewMouseDown" Margin="5" Padding="5" FontSize="18"></Button>
                </Grid>
                <RichTextBox x:Name="txtInfo" Margin="5" MinHeight="100"  ></RichTextBox>
            </StackPanel>
        </Grid>
    </Grid>
</Window>
    public partial class RoutedEventsWin : Window
    {
        public RoutedEventsWin()
        {
            InitializeComponent();
        }

        private void btn1_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            this.txtInfo.AppendText(string.Format("当前响应事件对象:{0},响应事件原始对象:{1}\r\n", (sender as FrameworkElement).Name, e.OriginalSource));
        }
    }
image.png
  • 直接事件(Direct):
    只有源元素本身才有机会调用处理程序以进行响应,这些事件不经过路由过程,而是直接在触发它们的元素上发生,不会在元素树中传播。
// 示例:直接添加事件处理程序到直接事件
private void Button_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Button was clicked");
}
 
// 在构造函数中注册事件处理程序
public MainWindow()
{
    InitializeComponent();
    this.someButton.Click += new RoutedEventHandler(Button_Click);
}
  • 阻止消息的传递
    在开发过程中,如果希望事件消息在某个事件触发之后,路径上的对应事件不再触发,可以在节点事件函数中,将Handled属性设置为true。
    WPF中,所有的路由事件都共享一个公共事件基类RoutedEventArgs,该类中定义了有Handled属性,Handle属性用于告诉消息系统,这个事件是否已经被处理,如果是,则后续事件不需要再对该消息进行处理。
<Window x:Class="WPFLearn.Other.RoutedEvents.RoutedEventsWin"
        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:WPFLearn.Other.RoutedEvents"
        mc:Ignorable="d"
        Title="RoutedEventsWin" Height="450" Width="800">
    <Grid>
        <StackPanel HorizontalAlignment="Left" ButtonBase.Click="StackPanel_Click" VerticalAlignment="Center" Width="200" Height="300">
            <Button Content="按钮1" Click="Button1_Click"/>
            <Button Content="按钮2" Click="Button2_Click"/>
            <Button Content="按钮3"/>
        </StackPanel>
    </Grid>
</Window>
public partial class RoutedEventsWin : Window
{
    public RoutedEventsWin()
    {
        InitializeComponent();
    }


    #region 冒泡路由事件
    private void StackPanel_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("StackPanel按钮点击!");
    }

    private void Button1_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("按钮1点击!");
        e.Handled = true;//阻止继续冒泡
    }

    private void Button2_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("按钮2点击!");
    }
    #endregion
}

附加事件

路由事件的宿主是那些有 UI 显示功能的界面元素,而附加事件是那些没有 UI 显示功能的元素,其本质还是路由事件,只是路由事件的宿主不一样。附加事件只是路由事件的一种用法而已,比如依赖属性和附加属性。
常见的附加事件有:

  • Binding类:SourceUpdated事件、TargetUpdated事件。
  • Mouse类:MouseEnter事件、MouseLeave事件、MouseDown事件、MouseUp事件等。
  • Keyboard类:KeyDown事件、KeyUp事件等。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 本文介绍1.路由策略2.冒泡事件3.隧道事件4.直接事件5.自定义事件 路由策略 WPF路由事件的策略分为以下三种...
    Elvis523阅读 4,560评论 0 1
  • 理解路由事件 路由事件是一种可以针对元素树中的多个侦听器而不是仅仅针对引发该事件的对象调用处理程序的事件,也就是说...
    东南有大树阅读 3,502评论 0 1
  • 事件和委托参见C#事件和委托路由事件是WPF中特有的,独特的事件,允许事件在元素树中进行路由。 传统的事件模式,事...
    c624ef3ffb3f阅读 1,345评论 0 0
  • 此文章并不能构成体系,仅作为个人学习笔记使用 Dart 语言 一、特性 1,一切皆对象Dart 是纯对象的编程语言...
    呐呐呐nana阅读 656评论 0 0
  • 普通CLR事件: 事件的拥有者:即消息的发送者。事件的宿主可以在某些条件下激发它拥有的事件,事件被触发则消息被发送...
    Ch_Running阅读 4,132评论 0 0