逐步边缘化的二哥,深入显出WPF

图片 4

逐步边缘化的二哥,深入显出WPF

事件是C#的底工之风流倜傥,学好事件对于精晓.NET框架大有利润。

  假如对事件一点都不明白还是是反反复复的话,建议先去看张子阳的寄托与事件的篇章(相比长,也许看完了,也记不清看那生机勃勃篇了,没事,小编会原谅你的卡塔尔国,废话相当的少说,开头走入正题。本记录不是记录古板的风浪(CLCR-V事件卡塔 尔(英语:State of Qatar),而是记录WPF中常用的事件——路由事件,由于路由事件“传播”的大运是沿着可视树传播的,所以在记录最早在此之前依然询问一下逻辑树和可视树。

事件最普及的比喻正是订阅,即,纵然你订阅了自个儿的博客,那么,当自家公布新博客的时候,你就能获取照应。

 生机勃勃、逻辑树和可视树

  在WPF中有二种树:逻辑树(Logical Tree卡塔尔和可视树(Visual
Tree卡塔尔,XAML是抒发WPF的生龙活虎棵树。逻辑树完全部是由布局组件和控件构成。如若大家把逻辑树延伸至Template组件等级,我们就拿到了可视树,所以可视树把树分的更紧凑。由于本记录重在记录事件,所以不做过多揭橥逻辑树和可视树的内容。关于逻辑树和可视树的界别能够参照。

而以此进程正是事件,只怕说是事件运行的轨迹。

二、路由事件

事件是散落,以自己的博客为骨干,向全部订阅者发送新闻。我们把这种分散称之为[多播]。

2.1、小记事件

  即便看完了寄托与事件的文章,相信会对事件有更进一层的认知了,但要么要把某些幼功的地点再记录一下。叁个平地风波要有下边几个成分,才会变的有含义:

    • 事件的具备者(sender卡塔尔——即音信的发送者。
    • 事件发送的音讯(伊夫ntAgs卡塔 尔(英语:State of Qatar)
    • 事件的响应者——音信的收信人(对象卡塔 尔(阿拉伯语:قطر‎。
    • 响应者的微型机——信息的选拔者要对消息作出管理的法子(方法名卡塔尔。
    • 响应者对发送者事件的订阅

最数见不鲜的事件用项是窗体编制程序,在Windows窗体应用程序和WPF应用程序中。

2.2 初试路由事件

  大家创建二个winform项目,然后在窗体上增加二个开关,双击增多八个Computer,会发掘private
void btn_Click(object sender, EventArgs
e)微处理机要管理音信是伊夫ntArgs类型的,这里对应的是守旧的轩然大波(大家叫它CL奥迪Q7事件卡塔尔国。相似大家成立三个WPF项目,然后增多叁个开关,双击增添一个Computer,会发觉private
void Button_Click(object sender, Routed伊夫ntArgs
e)微型机要管理的音信是RoutedEventArgs类型的,这里对应的是路由事件。两个有怎样区别吗。让大家先看看CL奥迪Q3事件的弊病,就像(this.btn.Click
+= new
System.EventHandler(this.btn_Click);卡塔 尔(阿拉伯语:قطر‎每一个音信都以从发送到响应的一个进度,当一个Computer要用数10次,必需树立显式的点对点订阅关系(窗体对开关事件的订阅,如果是再有一个开关的话,就要再来一遍订阅卡塔尔国;还大概有一个害处是:事件的宿主必需能够一贯访谈事件的响应者,不然不可能构建订阅关系(如有五个零件,点击组件意气风发的按键,想让组件二响应事件,那么就让组件二向组件生机勃勃的按键暴光三个得以访谈的风云,那样只要再多多少个嵌套,会并发事件链,有暴光就算拆穿不当就存在着威逼卡塔 尔(英语:State of Qatar)。路由事件除了能很好的减轻地点的主题材料,还会有贰个是路由事件在有路的情状下,能很好的根据规定的方法传播事件,因为XAML的树状结构,构成了一条条的征程,所以在WPF中,引进了路由事件。比方:假设窗体要以相像的主意管理五个按键的平地风波,我们就足以用一句代码就化解了,this.AddHandler(Button.Click伊芙nt,
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 Routed伊芙ntHandler(this.ButtonClicked));订阅事件时,第二个参数是路由事件类型,在那间用的是Button的Click伊夫nt,就好像正视属性同样,类名加上信赖属性,这里是类名加上路由事件。别的三个是e.OriginalSource与e.Source的分别。由于消息每传一站,都要把新闻交个一个控件(此控件成为了信息的殡葬地方卡塔尔国,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奔驰G级事件包装,3.创办能够激情路由事件的秘技。纪念一下依赖属性,前多个步骤应该和路由事件很平常吧。上面将多少个步骤分开来申明:

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

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

其次步:为路由事件增添CL路虎极光事件包装

       /*包装事件
        *这里与传统的数据差别是把+=和-=换成了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方法的的参数应当要平等,不然会报错,能够把下部一句中的ReportTimeRoute伊夫ntHandler换成Routed伊夫ntHandler试试,汇合世:不可能从文本“ReportTimeHandler”创制“ReportTime”。”,行号为“5”,行任务为“30”,的标题,这里主要的原由便是寄托的参数和Routed伊芙ntHandler的参数不平等,就算都以(sender, 
e);可是e的门类已经发出了转移,成为了ReportTime伊芙ntArgs类型的。所以在应用早先,声美赞臣个信托就能够了。还应该有个格局是利用EventHandler<ReportTime伊芙ntArgs>替换ReportTimeRouteEventHandler,其实两侧的用法大概,只是差异的写法,但是是本身倍感第后生可畏种写法会更加好通晓。具体有关EventHandler<ReportTimeEventArgs>的含义请参照他事他说加以考查。我们一样能够选取让第三个参数更换成其它三种档期的顺序的探视测量检验结果。

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关键字。

是的,事件的定义正是这么,因为要声明一个风浪,须要多个因素:

风度翩翩,标志提供对事件的响应的方法的委托。

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

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

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

如代码所示,大家选择了+=这么些标志来为事件赋值,赋值的内容是三个信托和一个函数。

里面+=大家将她掌握为【加多】。

代码中,大家选拔三种赋值方式,但骨子里都以为事件test伊夫nt增添叁个委。

其次种将函数直接【增添】到事件中,编写翻译时也会把函数转变到委托【加多】到事件中。

系统提供事件

C#的框架都很卓越,而各种卓越框架都为大家提供了部分经文事件。

逐步边缘化的二哥,深入显出WPF。由于事件必须[标志响应措施的委托],所以这个事件所运用的嘱托都有叁个一齐的特点,命名中涵Guy夫nt。

比如EventHandler,CancelEventHandler,RoutedEventHandler,ContextMenuEventHandler等。

当中最优异的正是伊芙ntHandler和RoutedEventHandler。

EventHandler:

EventHandler定义如下

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

她带有了多个参数,即当大家为事件增多伊夫ntHandler委托后,再去触发该事件;被触发的寄托将拿到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,它们一个是发送者,四个是源。

在EventHandler中,sender即source,因为它是直接事件。而在冒泡事件中,sender不一定等于source。即发送者不必然是源。

下边大家用WPF来拜望路由事件。

笔者们第黄金时代在XAML页面定义叁个RadioButton开关,然后设置他的模板是Button。然后分别定义各自的Click方法。

Xaml页面如下:

 <RadioButton Click="btnParent_Click">
            <RadioButton.Template>
                <ControlTemplate>
                    <StackPanel>
                        <TextBlock Text="我的名字" ></TextBlock>
                        <Button Content="Kiba518"   Click="btnClild_Click" ></Button>
                    </StackPanel>
                </ControlTemplate>
            </RadioButton.Template> 
</RadioButton> 

cs文件事件如下:

 private void btnParent_Click(object sender, RoutedEventArgs e)
 {
     string type = sender.GetType().ToString();//RadioButton
 }

 private void btnClild_Click(object sender, RoutedEventArgs e)
 {
     string type = sender.GetType().ToString();//Button
 }

运作起来,我们点击按键,通过断点我们可以见见,我们点击的按键触发了btnClild_Click和btnParent_Click事件

梯次是先btnClild_Click后btnParent_Click。

透过获取sender的类型,小编也能够看出,btnClild_Click的sender类型是Button,而btnParent_Click的sernder类型是RadioButton。

事件驱动编制程序

事件驱动编制程序那个定义给自个儿的认为很怪,因为直接用C#,而C#的成都百货上千框架都以事件驱动的,所以平素感觉事件驱动是自然。

而当事件驱动设计那一个词平常出现后,反而感觉蹊跷。

就恍如,每四日吃粳米饭,猛然有一天,全体人都在说籼米饭好香的感觉相似,你生龙活虎听就感到奇异。

因为事件驱动对于C#支出来说,实在太普通了。当然,那也得益于微软框架做的实乃太好了。

之所以,作者也不明白怎样在C#里讲事件驱动编制程序。因为使用C#的框架正是运用事件驱动编制程序。

事件和寄托到底是什么关联?

事件是用来多播的,而且用委托来为事件赋值,可以说,事件是依靠委托来兑现的。

但委托中也可能有多播,这为何要单独弄出来三个事变吧?

第风度翩翩,存在即合理,事件一定有他存在的含义。 

事件存在的意义

自家对事件存在的意思是那样掌握的。大家在C#编排框架时,差不离不用委托的多播,因为委托的多播和事件存在严重的二义性。就算编写框架的人学会了选拔委托的多播,但使用框架的同事也许并还不太理解,何况C#框架中,超多是使用事件来开展多播的。

之所以委托的多播和事件联合使用的框架,会形成选拔那些框架的低等开辟者相当多迷惑,而这种嫌疑,会发生超级多不必要的题目。

譬如,
你定义了多少个信托,另四个开荒者用那个委托做了个多播,当第八个开拓者来保证这段代码时,假如她是新手,不理解委托的多播,那就很有希望只校订了信托调用的代码。而尚未去联合多播那几个委托的代码。那系统就时有爆发了藏匿的bug。

那么,事件和信托到底是什么样关系啊?

事件与寄托的确存在复杂的涉及,怎么讲都以情有可原的。但,C#开垦者只供给记住,他们俩不妨就可以。在C#事件是事件,委托是信托。两个就犹如int和string相仿,没有别的关系。

由来很简短,学习的进程中尽量裁减概念混淆。并且,在C#支出中,好的架构者也平淡无奇会将事件和寄托分离,所以,就觉着事件和委托未有涉及就能够。

结语

骨子里事件很好掌握,一点不复杂。小编在写那篇小说的历程中,也没悟出如何特其他只怕说相比较高端的用法。

但诚实的利用项景中,小编的感到到是,随着MVVM的成长,事件实际在被日益放任。尽管微软做了重重经文的事件驱动框架。但那都是病故了。

诸如WPF固然协理事件驱动,但MVVM在WPF下的表现号称完美,所以WPF下的事件差不离从未人用了。

再比方前端的Angularjs等框架,提供了上流的MVVM使用效果,也让新的前端设计员渐渐舍弃了风浪。

就此,事件在现在的编制程序中,非常的大概将不在有那么重大的地位了。但学好事件,对于我们掌握微软框架,依然有极大帮扶的。

C#语法——元组类型

C#语法——泛型的有余施用

C#语法——await与async的科学张开药方式

C#语法——委托,架构的血液

我对C#的认知。


注:此随笔为原创,迎接转发,请在文章页面鲜明地点给出此文链接!
若您以为那篇小说还不易,请点击下右下角的【推荐】,特别感激!
若果您认为这篇文章对你有所扶助,那就不要紧支付宝小小打赏一下呢。 

图片 4

 

admin

网站地图xml地图