深入浅出WPF,逐渐边缘化的大哥

事件是C#的底工之生龙活虎,学好事件对于明白.NET框架大有益处。

  假如对事件一点都不精晓照旧是含含糊糊的话,提议先去看张子阳的委托与事件的小说(相比长,或者看完了,也忘怀看那大器晚成篇了,没事,笔者会原谅你的卡塔 尔(阿拉伯语:قطر‎,废话非常少说,初叶步向正题。本记录不是记录古板的事件(CLOdyssey事件卡塔 尔(英语:State of Qatar),而是记录WPF中常用的风浪——路由事件,由于路由事件“传播”的时日是本着可视树传播的,所以在笔录早先以前依旧摸底一下逻辑树和可视树。

事件最习认为常的比喻正是订阅,即,倘让你订阅了本身的博客,那么,当自家颁发新博客的时候,你就能获得文告。

 风度翩翩、逻辑树和可视树

  在WPF中有两种树:逻辑树(Logical Tree卡塔 尔(阿拉伯语:قطر‎和可视树(Visual
Tree卡塔 尔(英语:State of Qatar),XAML是宣布WPF的意气风发棵树。逻辑树完全部是由布局组件和控件构成。假如大家把逻辑树延伸至Template组件等级,大家就拿到了可视树,所以可视树把树分的更紧凑。由于本记录重在笔录事件,所以不做过多公布逻辑树和可视树的源委。关于逻辑树和可视树的差别能够参见。

而以此历程就是事件,只怕说是事件运维的轨迹。

二、路由事件

事件是分散,以本人的博客为主导,向全体订阅者发送音信。大家把这种分散称之为[多播]。

2.1、小记事件

  若是看完了信托与事件的篇章,相信会对事件有更进一层的认知了,但依旧要把风姿罗曼蒂克部分底工的地点再记录一下。二个事件要有下边多少个成分,才会变的有含义:

    • 事件的具有者(sender卡塔尔国——即新闻的发送者。
    • 事件发送的新闻(伊芙ntAgs卡塔尔国
    • 事件的响应者——音讯的收信人(对象卡塔尔国。
    • 响应者的微处理机——消息的选用者要对消息作出管理的艺术(方法名卡塔尔国。
    • 响应者对发送者事件的订阅

最不可计数的风云用项是窗体编制程序,在Windows窗体应用程序和WPF应用程序中。

2.2 初试路由事件

  大家创建三个winform项目,然后在窗体上增添八个按键,双击加多多个Computer,会发觉private
void btn_Click(object sender, 伊芙ntArgs
e)微机要管理消息是伊芙ntArgs类型的,这里对应的是思想的事件(大家叫它CL瑞虎事件卡塔尔国。相似大家创建三个WPF项目,然后加多二个开关,双击增多二个Computer,会发觉private
void Button_Click(object sender, RoutedEventArgs
e)微处理机要拍卖的音信是Routed伊芙ntArgs类型的,这里对应的是路由事件。两个有怎么着分别呢。让大家先看看CL哈弗事件的坏处,就像(this.btn.Click
+= new
System.伊夫ntHandler(this.btn_Click);卡塔尔每二个音信都以从发送到响应的叁个经过,当贰个Computer要用数次,必需树立显式的点对点订阅关系(窗体对开关事件的订阅,尽管是再有一个开关的话,将要再来二回订阅卡塔尔国;还应该有叁个缺欠是:事件的宿主必得能够一向访谈事件的响应者,不然不能够建构订阅关系(如有多个零件,点击组件意气风发的开关,想让组件二响应事件,那么就让组件二向组件豆蔻年华的开关暴光八个方可访问的风云,那样风姿罗曼蒂克旦再多多少个嵌套,会师世事件链,有揭露假若揭露不当就存在着威吓卡塔 尔(英语:State of Qatar)。路由事件除了能很好的解决地点的标题,还会有二个是路由事件在有路的情景下,能很好的依照明确的法子传播事件,因为XAML的树状结构,构成了一条条的征程,所以在WPF中,引入了路由事件。比如:倘诺窗体要以相近的主意管理多个按键的风云,大家就能够用一句代码就解决了,this.AddHandler(Button.ClickEvent,
new
RoutedEventHandler(this.ButtonClicked));那样就缩小代码量。上边通过叁个例子初试一下路由事件。 给出XAML代码:

<Window x:Class="Chapter_06.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="GridRoot" Background="Lime">
        <Grid x:Name="gridA" Margin="10" Background="Blue">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Canvas x:Name="canvasLeft" Grid.Column="0" Background="Red" Margin="10">
                <Button x:Name="buttonLeft" Content="left" Width="40" Height="100" Margin="10"/>
            </Canvas>
            <Canvas x:Name="canvasRight" Grid.Column="1" Background="Yellow" Margin="10">
                <Button x:Name="buttonRight" Content="right" Width="40" Height="100" Margin="10" />
            </Canvas>
        </Grid>
    </Grid>
</Window>

  大家点击按钮时,无论是buttonLeft照旧buttonRight单击都能展现按键的称呼。七个开关到顶上部分的window有独一条路,左侧的按键对应的路:buttonLeft->canvasLeft->gridA->GridRoot->Window,左边开关对应的路:buttonRight->canvasRight->gridA->GridRoot->Window。假诺GridRoot订阅五个计算机,那么微型机应该是如出风姿洒脱辙的。后台代码为:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Chapter_06
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.GridRoot.AddHandler(Button.ClickEvent,new RoutedEventHandler(this.ButtonClicked));
        }
        private void ButtonClicked(object sender, RoutedEventArgs e)
        {
            MessageBox.Show((e.OriginalSource as FrameworkElement).Name);
        }
    }
}

  下边先解释一下路由事件是怎么沿着可视树来传播的,当Button被点击,Button就开始发送新闻了,可视树上的要素假如订阅了Button的点击事件,那么才会基于音信来作出相应的反应,如果未有订阅的话,就不在意它产生的消息,当然大家仍然是能够垄断它的新闻的传播形式,是从树根到树叶传播,依旧树叶向树根传播以至是直接达到目标传播,不止如此,仍是可以说了算音信传开有些成分时,甘休扩散。具体的会在后面记录到。其次是this.GridRoot.AddHandler(Button.ClickEvent,new RoutedEventHandler(this.ButtonClicked));订阅事件时,第二个参数是路由事件类型,在这里间用的是Button的ClickEvent,就像依赖属性同样,类名加上信任属性,这里是类名加上路由事件。别的七个是e.OriginalSource与e.Source的分别。由于新闻每传一站,都要把音信交个多个控件(此控件成为了新闻的发送地点卡塔 尔(英语:State of Qatar),e.Source为逻辑树上的源流,要想获取原始发音讯的控件(可视树的根源卡塔尔要用e.OriginalSource。最终验明正身一下,怎么在XAML中增添订阅事件。直接用<Grid
x:Name=”gridA” Margin=”10″ Background=”Blue”
Button.Click=”ButtonClicked”>在.net平台上不可能智能提示Button,因为Click是世襲与ButtonBase的事件,XAML只认得含有Click事件的要素,但是要破浪乘风的写下去技术得逞。运营方面代码,点击左侧按键,效果如图1:

 

图片 1

图1

暗中同意的路由新闻里面属性有七个,如图2,能够活动转到定义看一下其性情代表的意义。

图片 2

图2

 

当在窗体中点击按键,移动鼠标等事件时,相应的后台程序会收下公告,再实施代码。

 2.3自定义路由事件

  通过地方的小规模试制路由事件,应该适中由事件有个别领悟了,上边就记录一下如何自定义贰个路由事件。差不离分成三个步骤:1.扬言并注册路由事件,2.为路由事件增多CL奥迪Q5事件包装,3.成立可以激发路由事件的法子。回想一下重视属性,前八个步骤应该和路由事件很经常吧。上边将多个步骤分开来证明:

先是步:申明并注册路由事件       

       //***Event为路由事件名,类型为路由事件类型
       //注册事件用的是EventManager.RegisterRoutedEvent
      //第一个参数为事件名(下面的***都为同一个单词)
       //第二个参数为事件传播的策略,有三种策略:Bubble(冒泡式),Tunnel(隧道式),Direct(直达式)分别对应上面的三种青色字体的三种方式
       //第三个参数用于指定事件处理器的类型,该类型必须为委托类型,并且不能为 null。
       //第四个参数为路由事件的宿主    
       public static readonly RoutedEvent ***Event = EventManager.RegisterRoutedEvent("***", RoutingStrategy.Bubble,
                                                             typeof(***RouteEventHandler), typeof(ClassName));

第二步:为路由事件增加CL昂Cora事件包装

       /*包装事件
        *这里与传统的数据差别是把+=和-=换成了AddHandler和RemovedHandler
        */
        public event RoutedEventHandler ***
        {
            add { this.AddHandler(***Event, value); }
            remove { this.RemoveHandler(***Event, value); }
        }

其三步:创建能够慰勉路由事件的主意

        /*对于控件的事件,一般是重写宿主事件对应的方法(如Button的click事件和OnClick()方法相对应):新建消息,并把消息与路由事件相关联,
         *通过调用元素的RaiseEvent方法把时间传送出去(这里与包装器的CRL事件毫不相干),在CLR事件是用Invoke方法,下面以按钮为例
         */

        protected override void OnClick()
        {
            base.OnClick();
            ***EventArgs args = new ***EventArgs(***Event, this);
            this.RaiseEvent(args);
        }

 上面大家就来落实三个大致的自定义路由作用,当路由飘过二个控件的小时,呈现通过该控件的时间。 上边介绍的大概了,所以就径直上代码,有亟待表达的话,再一个个解说。

下面是XAML代码:

<Window x:Class="DefineEvent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DefineEvent" 
        Title="Routed Event" x:Name="window_1" Height="350" Width="525" local:TimeButton.ReportTime="ReportTimeHandler" >
    <Grid x:Name="grid_1" local:TimeButton.ReportTime="ReportTimeHandler" >
        <Grid x:Name="grid_2" local:TimeButton.ReportTime="ReportTimeHandler"  >
            <Grid x:Name="grid_3" local:TimeButton.ReportTime="ReportTimeHandler"  >
                <StackPanel x:Name="stackPanel_1" local:TimeButton.ReportTime="ReportTimeHandler" >
                    <ListBox x:Name="listBox" />
                    <local:TimeButton x:Name="timeButton" Width="200" Height="80" Content="显示到达某个位置的时间" ReportTime="ReportTimeHandler"/>
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
</Window>

下面是CS代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace DefineEvent
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    delegate void ReportTimeRouteEventHandler(object sender, ReportTimeEventArgs e);
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void ReportTimeHandler(object sender, ReportTimeEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            string timeStr = e.ClickTime.ToString("yyyyMMddHHmmss");
            string content = string.Format("{0}到达{1}", timeStr, element.Name);
            this.listBox.Items.Add(content);
        }
    }
    //创建消息类型,在此可以附加自己想要的信息
    public class ReportTimeEventArgs : RoutedEventArgs
    {
        public ReportTimeEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { }
        public DateTime ClickTime { get; set; }
    }
   public class TimeButton : Button
    {
        //1、为元素声明并注册事件
        public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Bubble,
                                                             typeof(ReportTimeRouteEventHandler), typeof(TimeButton));

       //2、包装事件
        public event RoutedEventHandler ReportTime
        {
            add { this.AddHandler(ReportTimeEvent,value); }
            remove { this.RemoveHandler(ReportTimeEvent,value); }
        }
        //3、创建激发事件的方法
        protected override void OnClick()
        {
            base.OnClick();
            ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this);
            args.ClickTime = DateTime.Now;
            this.RaiseEvent(args);
        }
    }
}

运作单击开关的功力为图3:

图片 3

图3

  注意下边包车型地铁一站式代码,在宣称和定义路由事件时,第八个参数,委托的门类和Computer方法的的参数应当要平等,不然会报错,能够把下部一句中的ReportTimeRouteEventHandler换来Routed伊夫ntHandler试试,会现身:不可能从文本“ReportTimeHandler”创立“ReportTime”。”,行号为“5”,行职分为“30”,的主题素材,这里关键的案由正是信托的参数和Routed伊夫ntHandler的参数不均等,即使都以(sender, 
e);不过e的品类已经产生了转移,成为了ReportTime伊夫ntArgs类型的。所以在利用从前,声美赞臣(Meadjohnson卡塔尔个寄托就足以了。还应该有个章程是利用伊夫ntHandler<ReportTime伊芙ntArgs>替换ReportTimeRouteEventHandler,其实双方的用法大致,只是不相同的写法,但是是本身认为到第一种写法会更加好明白。具体有关伊芙ntHandler<Report提姆eEventArgs>的意义请参见。咱们生机勃勃致还可以让首个参数退换成别的两体系型的探视测量试验结果。

public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Tunnel,
                                                             typeof(ReportTimeRouteEventHandler), typeof(TimeButton));

 要是大家愿意当事件传递到grid_2下面就甘休了,我们得以这么做:在ReportTimeHandler函数中加多代码。

            if (element.Name == "grid_2")
                e.Handled = true;

事件的定义

 三、总结

  本篇记录的内容就算相当少,可是认为记录的时间专门困难,重即使因为对事件的多少个组成都部队分还不是老大贯虱穿杨,并且在知道路由事件时,还要先明了逻辑树与可视树。最后还是把这大器晚成章看完了,但那么些只是初阶。

  文章紧要记录了路由事件的在可视树上的传播以至自定义路由事件的兑现。如若在篇章有不相同的理念或提议,应接调换! 下生机勃勃篇:《深入显出WPF》笔记——命令篇

 

法定对事件的求证是如此的:类或对象能够因而事件向任何类或对象文告发出的连带事务。

换来正常语言就是,事件能够定义成静态的或日常的,所以事件就足以由证明的目的调用,也得以直接通过类调用静态事件。

事件是C#中的风流倜傥种档期的顺序,除了框架为大家定义好的风云外,大家还是能自定义事件,用event关键字来声称。

上面大家来看最底蕴的事件定义。

public delegate void TestDelegate(string message);                                                  
public event TestDelegate testEvent;

咱俩先是定义了一个委托,然后使用event关键字,定义三个事变。

全部上看,好像即是在概念二个寄托,只是在委托的定义早先,加了个event关键字。

不容争辩,事件的定义就是如此,因为要声Bellamy个平地风波,需求多个成分:

生机勃勃,标志提供对事件的响应的主意的寄托。

二,一个类,用存储事件的数码。即,事件要定义在类中。

上面大家来为那几个事件赋值。

public void Init()
{   
    testEvent += new TestDelegate(EventSyntax_testEvent); 
    testEvent += EventSyntax_testEvent; 
}
private void EventSyntax_testEvent(string message)
{
    Console.WriteLine(message);
}

如代码所示,我们应用了+=那一个符号来为事件赋值,赋值的内容是二个委托和三个函数。

个中+=大家将她领会为【增添】。

代码中,大家运用二种赋值形式,但骨子里都感觉事件testEvent增加一个委。

第三种将函数间接【增加】到事件中,编写翻译时也会把函数转换到委托【增加】到事件中。

系统提供事件

C#的框架都很精髓,而各样非凡框架都为大家提供了部分精髓事件。

鉴于事件必需[标志响应措施的委托],所以这个事件所运用的寄托都有三个协同的表征,命名中隐含Event。

比如EventHandler,CancelEventHandler,RoutedEventHandler,ContextMenuEventHandler等。

中间最精髓的就是EventHandler和Routed伊芙ntHandler。

EventHandler:

EventHandler定义如下

[SerializableAttribute]
[ComVisibleAttribute(true)]
public delegate void EventHandler(
 object sender,
 EventArgs e
)

他包括了多少个参数,即当大家为事件加多EventHandler委托后,再去触发该事件;被触发的委托将获得object
sender和伊夫ntArgs e五个参数。

sender:代表源,即触发该事件的控件。

e:代表事件参数,即触发该事件后,事件为被触发的信托,传递了有的参数,以有协助委托在拍卖数据时,更便捷。

传说那一个原理,咱们能够深入分析出大多事物。

诸如,当控件DataGrid的风浪被触发时,只要查看一下sender的真正类型,就能够领略,到底是DataGrid触发的风浪,依旧DataGridRow或DataGridCell触发的了。

RoutedEventHandler:

RoutedEventHandler即路由事件,他的概念如下

public delegate void RoutedEventHandler(
 Object sender,
 RoutedEventArgs e
)

RoutedEventHandler也为大家提供了sender和e五个参数。

但RoutedEventHandler非常之处是,他的sender并不一定是动真格的的源,因为她是贰个冒泡路由事件,即上涨事件。

此间借使大家有好奇心去看官方文档,那么会在连锁的牵线中看看八个单词sender和source。

由此那七个单词,大家会清楚的精晓路由事件。轻易描述一下sender和source,它们二个是发送者,二个是源。

发表评论

电子邮件地址不会被公开。 必填项已用*标注