zoukankan      html  css  js  c++  java
  • 《WPF程序设计指南》读书笔记——第9章 路由输入事件

    1.使用路由事件

        路由事件是一种可以针对元素树中的多个侦听器(而不是仅针对引发该事件的对象)调用处理程序的事件。通俗地说,路由事件会在可视树(逻辑树是其子集)上,上下routed,如果哪个节点上订阅了事件,就会被触发。

        路由事件的规则有三种:

        (1)冒泡;由事件源向上沿视觉树传递一直到根元素。如 MouseDown

        (2)直接;只有事件源才有机会响应事件,某个元素引发事件后,不传递到其他元素

        (3)隧道;从元素树的根部调用事件处理程序并依次向下深入直到事件源。 一般情况下,WPF提供的输入事件都是以隧道/冒泡对实现的。隧道事件常常被称为Preview事件。如 PreviewMouseDown

        可以通过 e.handled = true 来停止路由。

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Documents;
    
    namespace LY.ExamineRoutedEvents
    {
        public class ExamineRoutedEvents : Application
        {
            static readonly FontFamily fontfam = new FontFamily("宋体");
            const string strformat = "{0,-30},{1,-15},{2,-15},{3,-15}";
            StackPanel stackOutput;
            DateTime dtLast;
            [STAThread]
            public static void Main()
            {
                ExamineRoutedEvents app = new ExamineRoutedEvents();
                app.Run();
            }
            protected override void OnStartup(StartupEventArgs e)
            {
                base.OnStartup(e);
                Window win = new Window();
                win.Title = "Examine Routed Events";
                //建立Grid
                Grid grid = new Grid();
                win.Content = grid;
                //建立三行
                RowDefinition rowdef = new RowDefinition();
                rowdef.Height = GridLength.Auto;
                grid.RowDefinitions.Add(rowdef);
    
                rowdef = new RowDefinition();
                rowdef.Height = GridLength.Auto;
                grid.RowDefinitions.Add(rowdef);
    
                rowdef = new RowDefinition();
                rowdef.Height = new GridLength(100, GridUnitType.Star);
                grid.RowDefinitions.Add(rowdef);
    
                //建立button,加入到grid
                Button btn = new Button();
                btn.HorizontalAlignment = HorizontalAlignment.Center;
                btn.Padding = new Thickness(24);
                btn.Margin = new Thickness(24);
                grid.Children.Add(btn);
                //建立textblock,加入button
                TextBlock text = new TextBlock();
                text.FontSize = 24;
                text.Text = win.Title;
                btn.Content = text;
                //建立标题,显示在ScrollViewer
                TextBlock textHeadings = new TextBlock();
                textHeadings.FontFamily = fontfam;
                textHeadings.Inlines.Add(new Underline(new Run(string.Format(strformat,
                    "Routed Events", "Sender", "Source", "OriginalSource"))));
                grid.Children.Add(textHeadings);
                Grid.SetRow(textHeadings, 1);
                //加入Scorllviewer
                ScrollViewer scroll = new ScrollViewer();
                grid.Children.Add(scroll);
                Grid.SetRow(scroll, 2);
                //建立Stackpanel,放入Scorllviewer
                stackOutput = new StackPanel();
                scroll.Content = stackOutput;
                //新增事件处理器
                UIElement[] els = { win, grid, btn, text };
                foreach (UIElement el in els)
                {
                    // Keyboard
                    el.PreviewKeyDown += AllPurposeEventHandler;
                    el.PreviewKeyUp += AllPurposeEventHandler;
                    el.PreviewTextInput += AllPurposeEventHandler;
                    el.KeyDown += AllPurposeEventHandler;
                    el.KeyUp += AllPurposeEventHandler;
                    el.TextInput += AllPurposeEventHandler;
    
                    // Mouse
                    el.MouseDown += AllPurposeEventHandler;
                    el.MouseUp += AllPurposeEventHandler;
                    el.PreviewMouseDown += AllPurposeEventHandler;
                    el.PreviewMouseUp += AllPurposeEventHandler;
                    
                    // Stylus
                    el.StylusDown += AllPurposeEventHandler;
                    el.StylusUp += AllPurposeEventHandler;
                    el.PreviewStylusDown += AllPurposeEventHandler;
                    el.PreviewStylusUp += AllPurposeEventHandler;
                    //Window,Grid,Textblock类没有click事件,所以用在ButtonBase类中定义的AddHandler方法
                    el.AddHandler(Button.ClickEvent,
                        new RoutedEventHandler(AllPurposeEventHandler));               
                }
                win.Show();
            }
    
            void AllPurposeEventHandler(object sender, RoutedEventArgs e)
            {
                //如果有时间空隙,加入空格
                DateTime dtNow = DateTime.Now;
                if (dtNow - dtLast > TimeSpan.FromMilliseconds(100))
                    stackOutput.Children.Add(new TextBlock(new Run()));
                dtLast = dtNow;
                //显示事件信息
                TextBlock text = new TextBlock();
                text.FontFamily = fontfam;
                text.Text = string.Format(strformat, e.RoutedEvent.Name,
                    TypeWithoutNamespace(sender), 
                    TypeWithoutNamespace(e.Source),
                    TypeWithoutNamespace(e.OriginalSource));
                stackOutput.Children.Add(text);
                (stackOutput.Parent as ScrollViewer).ScrollToBottom();
            }
            string TypeWithoutNamespace(object obj)
            {
                string[] str = obj.GetType().ToString().Split('.');
                return str[str.Length-1];
            }
        }
    }
    

     2.自定义路由事件 

        (1)声明并注册路由事件

        (2)为路由事件添加CLR事件包装

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

        以ButtonBase类中定义的Click路由事件为例:

    public abstract class ButtonBase : ContentControl, ICommandSource
    {
        //字段 声明路由事件
        public static readonly RoutedEvent ClickEvent;
        //构造函数 在静态构造函数中注册路由事件,也可在直接申明字段同时注册路由事件
        static ButtonBase()
        {
    	//第1个参数为事件名
    	//第2个参数为路由策略,有三种策略:Bubble(冒泡式),Tunnel(隧道式),Direct(直达式)
    	//第3个参数为用于指定事件处理器的类型,该类型必须为委托类型,并且不能为 null
    	//第4个参数为路由事件所在的类名
            ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ButtonBase));
        }
        //事件 将路由事件包装成CLR事件
        public event RoutedEventHandler Click
        {
            add { base.AddHandler(ClickEvent, value); }
            remove { base.RemoveHandler(ClickEvent, value); }
        }
        //方法 创建激发事件的方法
        protected virtual void OnClick()
        {
            RoutedEventArgs e = new RoutedEventArgs(ClickEvent, this);
            base.RaiseEvent(e);
        }
    }
    

      进一步可参考:http://www.cnblogs.com/wilderhorse/articles/3231454.html

         在进行上述自定义路由事件后,就可以像CLR事件一样,订阅事件和处理事件了。其中,订阅本身的事件同CLR事件一样,订阅子节点的事件用AddHandler方法,如上面“使用路由事件”中的例子。

  • 相关阅读:
    对“一道有趣的题目”的解答
    在Debian中使用Notify
    Debian服务器安装详细流程
    Lighttpd1.4.20源码分析之插件系统(1)plugin结构体和插件接口
    Lighttpd1.4.20源码分析之etag.c(h) HTTP/1.1中的Etag域
    Lighttpd1.4.20源码分析之bitset.c(h) 位集合的使用
    伸展树
    GCC中的弱符号与强符号
    Lighttpd1.4.20源码分析之fdevent系统(3) 使用
    Lighttpd1.4.20源码分析之插件系统(3)PLUGIN_TO_SLOT宏
  • 原文地址:https://www.cnblogs.com/pemp/p/3307332.html
Copyright © 2011-2022 走看看