WPF 路由事件

 最近因为工作需要学习WPF方面的知识,因为以前只关注的是B/S架构的东西,可是没想到参加工作的第一个项目竟然是C/S架构的WPF方面的开发,因为Web方面主要是请求响应模型,没有事件这个东西,在学习webform时虽然是基于事件模型的也有没有认真的研究事件,因为它已经逐渐被mvc或者其他方式(比如ashx和jquery easyui等类似的)替代,现在是CS架构了,需要把这块知识补上。

1、简单的事件模型

       事件的前身是消息,消息的本质就是一组数据记录这要执行的操作,然后消息处理函数根据消息的数据执行相应的操作,那么在消息处理函数中就充斥这大量的判断或者switch,这样对于大型应用程序的开发带来了不少麻烦。为了简单的开发微软封装了一套简单的事件模型。以前了解过window form的应该都知道,当托一个按钮到窗体后然后双击按钮就可以在.cs代码自动生成有关事件的代码,这就是一个简单的事件模型

事件模型包括一下几个部分,我们对应到一个winform程序的Demo中如下:

事件的拥有者:就是按钮:button1

事件:就是button1.Click,在Form1.cs中

事件的处理器就是这个方法button1_Click

订阅关系:也就是说事件和事件处理器如何建立联系的呢:

打开Form1.Designer.cs找答案

 this.button1.Click += new System.EventHandler(this.button1_Click);

这里就建立了事件和事件处理器的联系,当然一个事件我们也可以定义多个处理器相应。

最后一个响应者:就是窗体本身

 

2、路由事件模型 

      传统的简单事件模型中,在消息激发是将消息通过事件订阅的然后交给事件的相应者,事件的相应者使用事件的处理器来做出相应,这样就存在一个问题,用户控件内部的事件就不能被外界订阅,因为事件的宿主必须能够直接访问到事件的响应者。

     路由事件的事件拥有者和事件的相应者之间则没有直接的显式订阅关系,事件的拥有者则只负责激发事件,事件将有谁相应它并不知道,事件的响应者则有事件的监听器,针对事件进行监听,当有此类事件传递至此事件响应者就使用事件处理器来相应事件并决定此事件是否继续传递。比如像上一个程序中的,点击“点我”以后事件就开始激发了,然后事件就会在控件树上进行传递,事件的响应者安装了监听器,当监听到这个事件进行响应,并决定这个事件是否继续传递。

    如果当事件在某个节点处理以后,不想让它继续传递,可以把它标记为“已处理”,就会停止路由,所有的路由事件都共享一个公共的事件数据基类 RoutedEventArgs。RoutedEventArgs 定义了一个采用布尔值的 Handled 属性。把事件设为已处理只要把Handled属性设为true即可

Xaml代码:

复制代码
<Window x:Class="RouteEventWpfDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="GridA">
        <Grid x:Name="GridB">
            <Grid x:Name="GridC">
                <Button Canvas.Left="101" Canvas.Top="68" Content="Button" Height="23" Name="ButtonA" Width="75" />
            </Grid>
        </Grid>
    </Grid>
</Window>
复制代码

xaml的交互逻辑代码

复制代码
  public partial class MainWindow : Window
    {/// <summary>
        /// 主窗口构造器
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
            //为GridA添加Button.ClickEvent监听
            this.GridA.AddHandler(Button.ClickEvent,new RoutedEventHandler(this.ButtonA_Click));
            //为GridB添加Button.ClickEvent监听
            this.GridB.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.ButtonA_Click));
            //为GridC添加Button.ClickEvent监听
            this.GridC.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.ButtonA_Click));
        }

        private void ButtonA_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(((Grid)sender).Name);
        }
复制代码

 

3、自定义路由事件

自定义路由事件大体需要三个步骤:

1、声明并注册路由事件

2、为路由事件添加CLR事件包装

3、创建可以激发路由事件的方法

Xaml代码

复制代码
<Window x:Class="MyRouteEventWpfDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:local="clr-namespace:MyRouteEventWpfDemo"
        local:TimeButton.ReportTime="ReportTimeHandler"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="GridA" local:TimeButton.ReportTime="ReportTimeHandler">
        <Grid x:Name="GridB" local:TimeButton.ReportTime="ReportTimeHandler">
            <Grid x:Name="GridC" local:TimeButton.ReportTime="ReportTimeHandler">
                <StackPanel x:Name="StackPanelA" local:TimeButton.ReportTime="ReportTimeHandler">
                    <ListBox x:Name="listBox"/>
                    <local:TimeButton x:Name="btnTime" Content="时间"  local:TimeButton.ReportTime="ReportTimeHandler" />
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
</Window>
复制代码

 自定义的ReportTimeEventArgs

复制代码
 /// <summary>
    /// 可以记录时间的RoutedEventArgs
    /// </summary>
    public class ReportTimeEventArgs : RoutedEventArgs
    {
        public ReportTimeEventArgs(RoutedEvent routedEvent, object source)
            : base(routedEvent, source)
        { }

        /// <summary>
        /// 记录点击时间
        /// </summary>
        public DateTime ClickTime { get; set; }
    }
复制代码

定义一个时间Button

复制代码
/// <summary>
    /// 自定义个一个时间控件
    /// </summary>
    public class TimeButton : Button
    {
        //声明并注册路由事件
        /*
         * 1、第一个参数ReportTime 为路由事件的名称
         * 2、第二个参数是路由事件的策略,包括Bubble冒泡式,Tunnel隧道式,Direct直达式(和直接事件类似)
         * 3、第三个参数用于指定事件处理器的类型
         * 4、第四个参数用于指定事件的宿主是哪一种类型
         */
        public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent
            ("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<ReportTimeEventArgs>), typeof(TimeButton));

        //CLR事件包装器
        public event RoutedEventHandler ReportTime
        {
            add { this.AddHandler(ReportTimeEvent, value); }
            remove { this.RemoveHandler(ReportTimeEvent, value); }
        }

        //激发路由事件,借用Click事件的激发方法
        protected override void OnClick()
        {
            base.OnClick();

            ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this);
            args.ClickTime = DateTime.Now;
            this.RaiseEvent(args);
        }        //CLR事件包装器
        public event RoutedEventHandler ReportTime
        {
            add { this.AddHandler(ReportTimeEvent, value); }
            remove { this.RemoveHandler(ReportTimeEvent, value); }
        }

        //激发路由事件,借用Click事件的激发方法
        protected override void OnClick()
        {
            base.OnClick();

            ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this);
            args.ClickTime = DateTime.Now;
            this.RaiseEvent(args);
        }
    }
}
复制代码

xaml的交互逻辑代码

复制代码
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

        }

        private void ReportTimeHandler(object sender, ReportTimeEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)sender;
            string timeStr = e.ClickTime.ToString("HH:mm:ss");
            string content = string.Format("{0}==>到达{1}", timeStr, element.Name);
            this.listBox.Items.Add(content);
        }
    }
复制代码

 4、附加事件

在wpf中还有一种事件附加事件,像前面说的路由事件它的事件宿主都是我们可以看到的界面元素,但是附加事件不具备显式在用户界面上的能力,比如一个文本框的改变,鼠标的按下,键盘的按下这些事件都是附加事件的例子

还是上面的例子我们给他加上颜色改变事件

在TimeButton类中添加如下代码

复制代码
        /// <summary>
        /// 声明颜色属性
        /// </summary>
        public string Color { get; set; }

        /// <summary>
        /// 声明并注册颜色改变路由事件
        /// </summary>
        public static readonly RoutedEvent ColorChangedEvent = EventManager.RegisterRoutedEvent
          ("ColorChanged", RoutingStrategy.Bubble, typeof(EventHandler<ReportTimeEventArgs>), typeof(TimeButton));

        /// <summary>
        /// 添加颜色改变事件
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        public static void AddColorChangedHandler(DependencyObject d, RoutedEventHandler e)
        {
            UIElement ui = (UIElement)d;
            if (ui != null)
            {
                ui.AddHandler(TimeButton.ColorChangedEvent, e) ;
            }
        }

        /// <summary>
        /// 删除颜色改变事件
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        public static void RemoveColorChangedHandler(DependencyObject d, RoutedEventHandler e)
        {
            UIElement ui = (UIElement)d;
            if (ui != null)
            {
                ui.RemoveHandler(TimeButton.ColorChangedEvent, e);
            }
        }
复制代码

MainWindow.cs改为

复制代码
public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //btnTime添加路由事件监听
            TimeButton.AddColorChangedHandler(this.btnTime, new RoutedEventHandler(this.ColorChangedHandler));
        }

        private void ReportTimeHandler(object sender, ReportTimeEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)sender;
            string timeStr = e.ClickTime.ToString("HH:mm:ss");
            string content = string.Format("{0}==>到达{1}", timeStr, element.Name);
            this.listBox.Items.Add(content);

            this.btnTime.Color = "123";
            //准备消息传给路由事件
            RoutedEventArgs args = new RoutedEventArgs(TimeButton.ColorChangedEvent, this.btnTime);
            //引发事件
            this.btnTime.RaiseEvent(args);
        }

        private void ColorChangedHandler(object sender, RoutedEventArgs e)
        {
            MessageBox.Show((e.OriginalSource as TimeButton).Color);
        }
    }
复制代码

 

参考:http://msdn.microsoft.com/zh-cn/library/ms742806.aspx

         http://msdn.microsoft.com/zh-cn/library/bb613550.aspx

----转载请注明出处http://www.cnblogs.com/JerryWang1991/  谢谢!
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
WPF中,自定义路由事件可以通过以下步骤实现: 1. 定义一个自定义路由事件: ``` public class MyRoutedEvent : RoutedEventArgs { public MyRoutedEvent() : base() { } public MyRoutedEvent(RoutedEvent routedEvent) : base(routedEvent) { } public MyRoutedEvent(RoutedEvent routedEvent, object source) : base(routedEvent, source) { } public string MyEventArgs { get; set; } // 自定义事件参数 public static readonly RoutedEvent MyEvent = EventManager.RegisterRoutedEvent("MyEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyRoutedEvent)); // 添加路由事件处理程序 public event RoutedEventHandler MyEventHandler { add { AddHandler(MyEvent, value); } remove { RemoveHandler(MyEvent, value); } } // 触发路由事件 public void RaiseMyEvent() { RaiseEvent(new RoutedEventArgs(MyEvent)); } } ``` 在这个例子中,我们定义了一个继承自RoutedEventArgs的类,并添加了一个自定义事件参数MyEventArgs。我们还定义了一个静态只读的MyEvent路由事件,并为它添加了一个路由事件处理程序MyEventHandler。最后,我们实现了一个RaiseMyEvent方法,该方法将触发MyEvent路由事件。 2. 在UI元素中使用自定义路由事件: ``` <Button Content="Click me" Click="Button_Click"/> ``` 在这个例子中,我们将按钮的Click事件绑定到Button_Click方法。在该方法中,我们可以创建一个MyRoutedEvent实例并触发它: ``` private void Button_Click(object sender, RoutedEventArgs e) { MyRoutedEvent myEvent = new MyRoutedEvent(); myEvent.MyEventArgs = "Hello World!"; RaiseEvent(myEvent); } ``` 在这个例子中,我们创建了一个MyRoutedEvent实例,并将MyEventArgs设置为“Hello World!”。然后,我们调用RaiseEvent方法触发MyEvent路由事件。 3. 在父控件中处理自定义路由事件: ``` <Grid local:MyRoutedEvent.MyEvent="Grid_MyEvent"> <!-- 子控件 --> </Grid> ``` 在这个例子中,我们将Grid控件的MyEvent路由事件绑定到Grid_MyEvent方法。在该方法中,我们可以获取到MyEventArgs的值: ``` private void Grid_MyEvent(object sender, RoutedEventArgs e) { MyRoutedEvent myEvent = (MyRoutedEvent)e; string myEventArgs = myEvent.MyEventArgs; // 处理自定义路由事件 } ``` 在这个例子中,我们获取MyRoutedEvent实例,并将其转换为MyRoutedEvent类型。然后,我们可以获取MyEventArgs的值并进行处理。 这就是一个简单的自定义路由事件的实现方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值