路由事件

Posted ~聪聪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了路由事件相关的知识,希望对你有一定的参考价值。

基础点

WPF支持直接事件,如 winform样直达事件。

最重要的是wpf支持路由事件,路由是通过“可视树”来传递的,而非逻辑树。

一个事件可以由多个事件处理器来处理(多播),一个事件处理器可以响应多个事件。

一个元素添加了事件侦听器,就不关心是谁触发的。

事件可以由触发元素一层一层向外传递,或由页面最外层一直往里传递,而非由元素开始才向内传。

事件的e.OriginalSource 为事件的可视树为源头,e.Source为逻辑树源头

事件就是定义一个路由事件,然后元素侦听此事件就行,相当于多播

 为元素添加事件侦听

  public partial class eventsim : Window

    {

        public eventsim()

        {

            InitializeComponent();

 

            //主要是通过AddHandler来添加对事件的侦听

            this.root.AddHandler(Button.ClickEvent, new RoutedEventHandler(buttonClick));

        }

 

        private void buttonClick(object sender, RoutedEventArgs e)

        {

            //使用e.OriginalSource获取触发者,而不是sender

            var source = e.OriginalSource as Button;

            MessageBox.Show(source.Name);

        }

    }

xaml里的写法

<Grid x:Name="root" Button.Click="buttonClick">

 

自定义路由事件

的写法

<Grid x:Name="grid1"  local:TimeButton.ReportTime="ReportTimeHandler">

        <Grid x:Name="grid2"  local:TimeButton.ReportTime="ReportTimeHandler">

            <Grid x:Name="grid3"  local:TimeButton.ReportTime="ReportTimeHandler">

     <local:TimeButton x:Name="time" Content="报时"  local:TimeButton.ReportTime="ReportTimeHandler"></local:TimeButton>

                <ListBox x:Name="listBox" H />

            </Grid>  

        </Grid>

    </Grid>

 

 

 public partial class eventsim : Window

    {

        public eventsim()

        {

            InitializeComponent();

        }

 

        private void ReportTimeHandler(object sender, TimeRouteEventArgs e)

        {

            var ele = sender as FrameworkElement;

            listBox.Items.Add(string.Format("事件到达{0},时间{1}",ele.Name,e.DateTime));

        }

         

    }

 

    //自定义事件触发后所携带的消息

    public class TimeRouteEventArgs : RoutedEventArgs

    {

        public TimeRouteEventArgs(RoutedEvent routedEvent, object source)

            :base(routedEvent, source)

        {   }

        public DateTime DateTime { get; set; }

    }

    //自定义一个按钮

    public class TimeButton : Button

    {

        //注册“报告时间”路由事件

        //变更名为"XXEvent",注册后事件名则为“XX”,这是默认的格式,要遵守

        public static readonly RoutedEvent ReportTimeEvent =

            EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<TimeRouteEventArgs>), typeof(TimeButton));

 

        //CLR事件包装器,就是给别人注册此事件使用

        public event RoutedEventHandler ReportTime

        {

            add { this.AddHandler(ReportTimeEvent, value); }

            remove { this.RemoveHandler(ReportTimeEvent, value); }

        }

 

        //激发路由事件,借助Click事件

        protected override void OnClick()

        {

            base.OnClick();//保留原始功能

 

            TimeRouteEventArgs arg = new TimeRouteEventArgs(ReportTimeEvent,this);

            arg.DateTime = DateTime.Now;

 

            //使用RaiseEvent激发事件

            this.RaiseEvent(arg);

        }

    }

 

参数说明:

public static readonly RoutedEvent ReportTimeEvent =

            EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<TimeRouteEventArgs>), typeof(TimeButton));

第一个:注册的事件名,一般为变更名不加Event

第二个:传递方向,Bubble由激发元素向外传,Tunnel由树根向内传,Direct直接事件

第三个:事件的处理器类型

第四个:事件的宿主

 

停止事件继续传递

      private void ReportTimeHandler(object sender, TimeRouteEventArgs e)

        {

            var ele = sender as FrameworkElement;

 

            if (ele.Name == "grid2")

                e.Handled = true;//停止继续传递

 

            listBox.Items.Add(string.Format("事件到达{0},时间{1}",ele.Name,e.DateTime));

        }

 

附加事件

路由事件的宿主是可视元素,而附加事件的宿主都是不可视元素,附加事件的触发还是得依靠元素的RaiseEvent。

 

例子:一个Student类,当名称变更时的事件

public partial class eventsim : Window

    {

        public eventsim()

        {

            InitializeComponent();

 

            //为元素grid1添加(student7的NageChange变更事件)侦听

            Student7.AddNameChangeHandler(grid1, new RoutedEventHandler(NameChangeEvenHandler));

        }

        //捕获变更后的事件处理器

        private void NameChangeEvenHandler(object sender,  RoutedEventArgs e)

        {

            MessageBox.Show((e.OriginalSource as Student7).Id.ToString());

        } 

 

        private void button_Click(object sender, RoutedEventArgs e)

        {

            Student7 stu = new Student7() { Id=1,Name="111"};

 

            stu.Name = "222";

 

       RoutedEventArgs arg = new RoutedEventArgs(Student7.NameChangeEvent, stu);//注意这里的source为stu,而非this 

            this.button1.RaiseEvent(arg);

        }

    }

 

    public class Student7

    {

        public int Id { get; set; }

        public string Name { get; set; }

 

 

        //定义路由事件

        public static readonly RoutedEvent NameChangeEvent =

          EventManager.RegisterRoutedEvent("NameChange", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Student7));

 

        //附加事件不能像路由事件那样添加CLR事件包装器,只能分开两个方法写

        //为界面元素添加路由事件侦听,注意命名规则

        public static void AddNameChangeHandler(DependencyObject d, RoutedEventHandler h)

        {

            UIElement e = d as UIElement;

            if (e != null)

            {

                e.AddHandler(Student7.NameChangeEvent, h);

            }

        }

        //移除侦听

        public static void RemoveNameChangeHandler(DependencyObject d, RoutedEventHandler h)

        {

            UIElement e = d as UIElement;

            if (e != null)

            {

                e.RemoveHandler(Student7.NameChangeEvent, h);

            }

        }

 

    }

以上是关于路由事件的主要内容,如果未能解决你的问题,请参考以下文章

WPF的路由事件冒泡事件隧道事件(预览事件)

WPF路由事件二:路由事件的三种策略

事件路由

WPF--路由事件

路由事件

2021-09-22 WPF上位机 38-路由事件