浅显WPF,委托和事件

事件是C#的底子之一,学好事件对于通晓.NET框架大有便宜。

  如若对事件一点都不打听依旧是笼统的话,提议先去看张子阳的嘱托与事件的文章(比较长,或者看完了,也忘怀看这一篇了,没事,作者会原谅你的),废话不多说,早先进入正题。本记录不是记录古板的轩然大波(CLPAJERO事件),而是记录WPF中常用的事件——路由事件,由于路由事件“传播”的时间是沿着可视树传播的,所以在笔录初阶从前依旧摸底一下逻辑树和可视树。

[转载]C#信托和事件(Delegate、伊芙nt、伊芙ntHandler、伊芙ntArgs),

一、了解C#中的预订义事件处理机制

事件最广泛的比方就是订阅,即,假如你订阅了作者的博客,那么,当自家发表新博客的时候,你就会收获关照。

 ① 、逻辑树和可视树

  在WPF中有二种树:逻辑树(Logical Tree)和可视树(Visual
Tree),XAML是抒发WPF的一棵树。逻辑树完全是由布局组件和控件构成。假诺大家把逻辑树延伸至Template组件级别,我们就获取了可视树,所以可视树把树分的更全面。由于本记录重在记录事件,所以不做过多宣布逻辑树和可视树的情节。关于逻辑树和可视树的区分可以参考。

初稿链接:

    
在写代码前我们先来熟练.net框架夹钟事件有关的类和寄托,通晓C#中预约义事件的拍卖。

而这几个历程便是事件,或然说是事件运行的轨迹。

② 、路由事件

14.1、委托

当要把办法作为实参传送给任何情势的形参时,形参要求利用委托。委托是一个门类,是三个函数指针类型,那么些连串将该信托的实例化对象所能指向的函数的细节封装起来了,即明确了所能指向的函数的签署,也正是限制了所能指向的函数的参数和再次来到值。当实例化委托的时候,委托对象会指向某2个非常的函数,实质正是将函数的地方赋值给了该信托的对象,然后就能够通过该信托对象来调用所指向的函数了。利用委托,程序员能够在委托对象中封装2个方法的引用,然后委托对象作为形参将被传给调用了被引用方法的代码,而不供给知道在编写翻译时刻具体是哪个方法被调用。

貌似的调用函数,大家都不会去选取委托,因为只要只是独自的调用函数,使用委托更麻烦一些;不过只要想将函数作为实参,传递给某些函数的形参,那么形参就肯定要动用委托来接纳实参,一般采纳方式是:在函数外面定义委托对象,并针对有些函数,再将这一个目的赋值给函数的形参,形参也是该委托类型的目的变量,函数里面再通过形参来调用所指向的函数。

     EventArgs是富含事件数量的类的基类,用于传递事件的细节。

事件是散落,以作者的博客为宗旨,向全数订阅者发送音信。大家把那种分散称之为[多播]。

2.① 、小记事件

  假如看完了委托与事件的篇章,相信会对事件有更进一步的认识了,但依旧要把部分基础的地点再记录一下。2个风云要有上边几个要素,才会变的有含义:

    • 事件的拥有者(sender)——即音讯的发送者。
    • 事件发送的新闻(伊夫ntAgs)
    • 事件的响应者——音信的接收者(对象)。
    • 响应者的电脑——新闻的接受者要对音讯作出处理的点子(方法名)。
    • 响应者对发送者事件的订阅

14.1.① 、定义委托

语法如下

delegate  result-type   Identifier ([parameters]);

说明:

result-type:重临值的种类,和方法的归来值类型一致

Identifier:委托的名号

parameters:参数,要引用的主意带的参数

小结

当定义了寄托随后,该信托的靶子自然可以同时也只可以指向该信托所界定的函数。即参数的个数、类型、顺序都要合营,重临值的品类也要合作。

因为定义委托也就是是定义二个新类,所以能够在定义类的别的位置定义委托,既能够在2个类的内部定义,那么此时即将通过此类的类名来调用那些委托(委托必须是public、internal),也足以在任何类的表面定义,那么此时在命名空间中与类的级别是平等的。依照定义的可知性,能够在委托定义上添加一般的拜会修饰符:当委托定义在类的外围,那么能够加上public、internal修饰符;固然委托定义到类的内部,那么能够添加public、 private、 protected、internal。一般委托都以概念在类的外界的。

     EventHandler是三个寄托证明如下

最广泛的轩然大波用途是窗体编制程序,在Windows窗体应用程序和WPF应用程序中。

2.2 初试路由事件

  大家树立三个winform项目,然后在窗体上添加三个按钮,双击添加3个电脑,会发现private
void btn_Click(object sender, 伊夫ntArgs
e)处理器要拍卖音信是伊夫ntArgs类型的,那里对应的是观念的事件(我们叫它CL奥迪Q5事件)。同样大家树立一个WPF项目,然后添加3个按钮,双击添加3个总括机,会发觉private
void Button_Click(object sender, Routed伊夫ntArgs
e)处理器要处理的音信是Routed伊芙ntArgs类型的,那里对应的是路由事件。两者有怎么样分化呢。让我们先看看CLLacrosse事件的弊端,就像(this.btn.Click
+= new
System.伊夫ntHandler(this.btn_Click);)每四个信息都以从发送到响应的叁个进程,当二个总结机要用数十次,必须树立显式的点对点订阅关系(窗体对按钮事件的订阅,如若是再有3个按钮的话,就要再来一遍订阅);还有三个害处是:事件的宿主必须能够一贯访问事件的响应者,不然无法建立订阅关系(如有多个零部件,点击组件一的按钮,想让组件二响应事件,那么就让组件二向组件一的按钮暴光一个得以访问的轩然大波,那样只要再多几个嵌套,会并发事件链,有揭穿假使暴光不当就存在着威胁)。路由事件除了能很好的缓解地点的问题,还有3个是路由事件在有路的图景下,能很好的遵照规定的方法传播事件,因为XAML的树状结构,构成了一条条的征程,所以在WPF中,引入了路由事件。举个例子:即使窗体要以相同的点子处理四个按钮的轩然大波,我们就能够用一句代码就消除了,this.AddHandler(Button.Click伊芙nt,
new
Routed伊夫ntHandler(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.Click伊夫nt,new Routed伊芙ntHandler(this.ButtonClicked));订阅事件时,首个参数是路由事件类型,在那边用的是Button的Click伊芙nt,就像正视属性一样,类名加上正视属性,那里是类名加上路由事件。别的三个是e.OriginalSource与e.Source的分别。由于音讯每传一站,都要把新闻交个1个控件(此控件成为了音讯的发送地方),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

 

14.1.二 、实例化委托

Identifier  objectName  =  new  Identifier( functionName);

实例化委托的精神正是将有些函数的地点赋值给委托对象。在此地:

Identifier :这些是委托名字。

objectName :委托的实例化对象。

functionName:是该信托对象所指向的函数的名字。对于这几个函数名要越发注意:定义那几个委托对象自然是在类中定义的,那么一旦所针对的函数也在此类中,不管该函数是静态还是非静态的,那么就直接写函数名字就可以了;假设函数是在别的类里面定义的public、

internal,可是倘倘若静态,那么就间接用类名.函数名,假诺是非静态的,那么就类的指标名.函数名,那些函数名与该目的是有涉及的,比如假若函数中出现了this,表示的正是对当下目的的调用。

          public delegate void EventHandler( object sender , EventArgs
e )

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

 2.3自定义路由事件

  通过上面的小规模试制路由事件,应该适用由事件有些驾驭了,上面就记下一下怎么自定义一个路由事件。大概分成七个步骤:1.注明并注册路由事件,2.为路由事件添加CLKoleos事件包装,3.创立可以激起路由事件的方式。纪念一下注重属性,前多个步骤应该和路由事件很一般吧。下边将五个步骤分开来表明:

首先步:评释并注册路由事件       

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

第3步:为路由事件添加CL福特Explorer事件包装

       /*包装事件
        *这里与传统的数据差别是把+=和-=换成了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);
        }

 上面大家就来兑现2个总结的自定义路由作用,当路由飘过二个控件的年华,展现通过该控件的大运。 上边介绍的几近了,所以就平昔上代码,有必要解释的话,再三个个解释。

下面是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

  注意上面包车型地铁一条龙代码,在注明和定义路由事件时,第6个参数,委托的品类和处理器方法的的参数一定要一致,不然会报错,能够把下边一句中的ReportTimeRoute伊夫ntHandler换成Routed伊夫ntHandler试试,会现出:不可能从文本“ReportTimeHandler”创造“ReportTime”。”,行号为“5”,行职分为“30”,的标题,那里根本的因由就是寄托的参数和Routed伊芙ntHandler的参数不平等,纵然都以(sender, 
e);可是e的类别已经发生了转移,成为了ReportTime伊芙ntArgs类型的。所以在使用在此之前,声贝拉米(Bellamy)个寄托就足以了。还有个点子是应用伊芙ntHandler<ReportTime伊芙ntArgs>替换ReportTimeRoute伊夫ntHandler,其实两边的用法差不离,只是差别的写法,不过是本人深感第叁种写法会更好通晓。具体有关伊夫ntHandler<ReportTime伊夫ntArgs>的意思请参见。大家一致能够行使让首个参数改变成其它两体系型的探视测试结果。

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;

14.1.叁 、委托预计

C#
2.0用委托揣摸增加了委托的语法。当大家要求定义委托对象并实例化委托的时候,就能够只传送函数的称号,即函数的地方:

Identifier  objectName  =  functionName;

这之中的functionName与14.1.2节中实例化委托的functionName是同一的,没什么不一样,满足上边包车型地铁规则。

C#编写翻译器创立的代码是平等的。编写翻译器会用objectName检测必要的委托项目,由此会成立Identifier委托类型的三个实例,用functionName即方法的地址传送给Identifier的构造函数。

注意:

亚洲必赢官网 ,不能够在functionName后边加括号和实参,然后把它传送给委托变量。调用方法一般会再次回到三个不可能加之委托变量的平常对象,除非那一个办法再次来到的是一个十分的信托对象。由此可见:只好把相匹配的法门的地址赋予委托变量。

委托估算能够在急需委托实例化的其余地点选择,就跟定义普通的信托对象是同一的。委托推测也能够用来事件,因为事件基于委托(参见本章前边的情节)。

    
注意那里的参数,前者是2个指标(其实那里传递的是指标的引用,如果是button1的click事件则sender正是button1),后边是富含事件数量的类的基类。

事件的定义

 三、总结

  本篇记录的内容纵然不多,可是觉得记录的小运专程困难,重若是因为对事件的多少个组成部分还不是可怜熟练,而且在知情路由事件时,还要先清楚逻辑树与可视树。最终还是把这一章看完了,但以此只是初叶。

  文章主要记录了路由事件的在可视树上的传播以及自定义路由事件的兑现。假使在文章有分歧的观点或提议,欢迎沟通! 下一篇:《深切浅出WPF》笔记——命令篇

 

14.1.肆 、匿名格局

到近来甘休,要想使委托工作,方法必须已经存在。但实例化委托还有其余一种办法:即经过匿名情势。

用匿名格局定义委托的语法与前边的定义并不曾差距。但在实例化委托时,就有分别了。上面是一个相当简单的控制台应用程序,表明了哪些利用匿名格局:

using System;

namespace Wrox.ProCSharp.Delegates

{

  class Program

  {

    delegate string DelegateTest(string val);

    static void Main()

    {

      string mid = “, middle part,”;

      //在形式中定义了办法

      DelegateTest  anonDel = delegate(string param)

      {

浅显WPF,委托和事件。        param += mid;

        param += ” and this was added to the string.”;

        return param;

      };

      Console.WriteLine(anonDel(“Start of string”));

    }

  }

}

信托DelegateTest在类Program中定义,它带二个字符串参数。有分其他是Main方法。在定义anonDel时,不是传递已知的法子名,而是选取2个回顾的代码块:它后面是最首要字delegate,后边是3个参数:

delegate(string param)

{

  param += mid;

  param += ” and this was added to the string.”;

  return param;

};

匿名格局的亮点是减掉了要编写的代码。方法仅在有嘱托行使时才定义。在为事件定义委托时,那是老大显明的。(本章后边研讨事件。)那有助于下落代码的扑朔迷离,特别是概念了有个别个事件时,代码会显得相比较不难。使用匿名方式时,代码执行得不太快。编写翻译器仍定义了二个办法,该办法只有三个机动钦点的名目,大家不须要了解这么些称号。

在动用匿名格局时,必须遵从七个规则:

一 、在匿超级模特式中不可能动用跳转语句跳到该匿名格局的外部,反之亦然:匿名情势外部的跳转语句不可能跳到该匿名格局的中间。

贰 、在匿名格局内部不能够访问不安全的代码。其它,也不能够访问在匿名情势外部使用的ref和out参数。但能够运用在匿超形式外部定义的别样变量。方法内部的变量、方法的参数能够随心所欲的行使。

假诺急需用匿名格局多次编辑同3个作用,就绝不选择匿名情势。而编写制定三个钦赐的法子相比较好,因为该措施只需编写二次,未来可由此名称引用它。

    
下边大家钻探一下Button类看看里面包车型地铁事件声明(使用WinCV工具查看),以Click事件为例。

法定对事件的注明是这么的:类或对象足以经过事件向别的类或对象公告发出的有关事务。

14.1.五 、多播委托

后边使用的各种委托都只包括一个办法调用,调用委托的次数与调用方法的次数相同,假设要调用四个格局,就必要反复给委托赋值,然后调用那么些委托。

信托也得以分包四个主意,那时候要向委托对象中添加多个章程,这种委托称为多播委托,多播委托有三个措施列表,假若调用多播委托,就能够接连调用两个章程,即先实施某一个形式,等该措施执行到位以往再实施此外2个办法,那一个艺术的参数都以一模一样的,那么些情势的实践是在三个线程中举行的,而不是每一个方法都以二个线程,最后将进行到位具有的情势。

假定选拔多播委托,就应留神对同一个委托调用方法链的各类并未正式定义,调用顺序是不明确的,不自然是比照添加办法的依次来调用方法,由此应防止编写制定重视于以一定顺序调用方法的代码。假设要想明显顺序,那么只可以是单播委托,调用委托的次数与调用方法的次数相同。

多播委托的次第艺术签名最棒是回来void;不然,就只能获得委托最终调用的3个办法的结果,而最终调用哪个方法是无能为力分明的。

多播委托的每3个方法都要与信托所界定的办法的重回值、参数匹配,不然就会有不当。

自笔者自个儿写代码测试,测试的结果眼下都是调用顺序和进入委托的逐条相同的,可是不拔除有两样的时候。 

delegate result-type Identifier ([parameters]); 

          public event EventHandler Click;

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

14.1.5.一 、委托运算符 =

Identifier  objectName  =  new  Identifier( functionName);

或者

Identifier  objectName  =  functionName;

那边的“=”号表示清空 objectName 的法子列表,然后将 functionName 出席到     objectName 的办法列表中。

     那里定义了2个伊芙ntHandler类型的风云Click

事件是C#中的一连串型,除了框架为大家定义好的轩然大波外,大家还足以自定义事件,用event关键字来声称。

14.1.5.② 、委托运算符 +=

objectName  +=  new  Identifier( functionName1);

或者

objectName  +=  functionName1;

此地的“+=”号表示在原有的章程列表不变的景况下,将 functionName1  到场到     objectName 的艺术列表中。能够在点子列表中添加八个一样的不二法门,执行的时候也会举办完全数的函数,哪怕有雷同的,就会一再举办同1个方法。

专注:objectName 必须是现已赋值了的,不然在概念的时候一向动用该符号:

Identifier  objectName    +=  new  Identifier( functionName1);或者

Identifier  objectName  +=  functionName1;就会报错。

    
前边的剧情都以C#在类库中曾经为大家定义好了的。上边大家来看编制程序时发出的代码。

上面大家来看最基础的轩然大波定义。

14.1.5.叁 、委托运算符 -=:

objectName  -=  new  Identifier( functionName1);

或者

objectName  -=  functionName1;

那边的“-=”号表示在 objectName 的办法列表中减去1个functionName1。能够在艺术列表中屡屡减去划一的主意,减一遍只会减二个主意,若是列表中无此办法,那么减就从未意义,对原来列表无影响,也不会报错。

在意:objectName 必须是早就赋值了的,不然在概念的时候一直行使该符号:

Identifier  objectName    -=  new  Identifier( functionName1);或者

Identifier  objectName  -=  functionName1;就会报错。

         private void button1_Click(object sender, System.EventArgs
e)
         {
             …
         }

public delegate void TestDelegate(string message);                                                  
public event TestDelegate testEvent;
14.1.5.4、委托运算符 +、-:

Identifier  objectName  =  objectName  + functionName1 –
functionName1;或者

Identifier  objectName  =  new  Identifier( functionName1) +
functionName1 – functionName1;

对此那种+、-表明式,在首先个记号+恐怕-的前边必须是信托而不可能是办法,前面包车型地铁+、-左右都不管。这么些不是绝对规律,还有待进一步的商量。

    
那是我们和button1_click事件所对应的不二法门。注意格局的参数符合委托中的签名(既参数列表)。那大家怎么把那一个法子和事件联系起来呢,请看下边包车型大巴代码。

我们率先定义了1个委托,然后使用event关键字,定义叁个轩然大波。

14.1.5.伍 、多播委托的不得了处理

透过2个寄托调用多个点子还有贰个大题材。多播委托包括3个各种调用的委托集合。借使因而信托调用的贰个格局抛出了特别,整个迭代就会停下。下边是MulticastIteration示例。在那之中定义了1个简约的寄托德姆oDelegate,它没有参数,重返void。这么些委托调用方法One()和Two(),那四个方法满意委托的参数和重临类型供给。注意方法One()抛出了二个十三分:

using System;

namespace Wrox.ProCSharp.Delegates

{

public delegate void DemoDelegate();

class Program

{

static void One()

{

Console.WriteLine(“One”);

throw new Exception(“Error in one”);

}

static void Two()

{

Console.WriteLine(“Two”);

}

在Main()方法中,创建了委托d1,它引用方法One(),接着把Two()方法的地方添加到同一个信托中。调用d1信托,就足以调用那四个办法。相当在try/catch块中捕获:

static void Main()

{

DemoDelegate d1 = One;

d1 += Two;

try

{

d1();

}

catch (Exception)

{

Console.WriteLine(“Exception caught”);

}

}

}

}

委托只调用了第二个艺术。第②个艺术抛出了老大,所以委托的迭代会结束,不再调用Two()方法。当调用方法的逐条没有点名时,结果会迥然不相同。

One

Exception Caught

注意:

多播委托包罗一个挨家挨户调用的嘱托集合。如果经过委托调用的一个艺术抛出了要命,整个迭代就会告一段落。即假设任一方法引发了很是,而在该办法内未捕获该尤其,则该特别将传递给委托的调用方,并且不再对调用列表中前边的章程开始展览调用。

在那种情状下,为了幸免那个题材,应手动迭代方法列表。Delegate类定义了艺术GetInvocationList(),它回到2个Delegate对象数组。今后得以选取那么些委托调用与信托直接相关的主意,捕获非凡,并一连下1次迭代。

static void Main()

{

DemoDelegate d1 = One;

d1 += Two;

Delegate[] delegates = d1.GetInvocationList();

foreach (DemoDelegate d in delegates)

{

try

{

d();

}

catch (Exception)

{

Console.WriteLine(“Exception caught”);

}

}

}

修改了代码后运转应用程序,相会到在捕获了尤其后,将持续迭代下3个办法。

One

Exception caught

Two

只顾:其实假若在多播委托的各类具体的章程中抓获非常,并在在那之中处理,而不抛出尤其,一样能落到实处多播委托的具有办法执行实现。那种艺术与地方格局的区分在于这种艺术的襄阳市在函数内处的,上面那种办法的可怜是在函数外面捕获并处理的。

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

完整上看,好像就是在概念贰个委托,只是在委托的定义从前,加了个event关键字。

14.1.六 、通过信托对象来调用它所针对的函数

① 、委托实例的名目,前边的括号中应蕴含调用该信托中的方法时采用的参数。

贰 、调用委托对象的Invoke()方法,Invoke前边的括号中应涵盖调用该信托中的方法时接纳的参数。

留神:实际上,给委托实例提供括号与调用委托类的Invoke()方法完全相同。因为Invoke()方法是寄托的一块儿调用方法。

 

瞩目:不管是多播委托依然单播委托,在尚未例外处理的情事下,在二个线程的施行进度中去调用委托(委托对象所指向的函数),调用委托的实践是不会新起线程的,那些执行或许在原线程中的,那么些对于事件也是一模一样的。当然,倘若是在委托所指向的函数里面去运营多个新的线程那便是其它3回事了。

     把this.button1_Click方法绑定到this.button1.Click风波。

是的,事件的概念便是这么,因为要声Bellamy个风浪,必要四个因素:

14.2、事件

    
上面大家商讨一下C#事件处理的做事流程,首先系统会在为大家创设3个在后台监听事件的对象(若是是
button1的事件那么监听事件的正是button1),这几个指标用来产生事件,倘诺有某些用户事件时有发生则发出相应的应用程序事件,然后实施订阅了风波的持有办法。

一,标识提供对事件的响应的情势的寄托。

14.2.壹 、自定义事件

贰 、简单的自定义事件(1)

二,三个类,用存款和储蓄事件的数目。即,事件要定义在类中。

14.2.1.一 、声圣元(Synutra)个信托:

Delegate result-type delegateName ([parameters]);

那一个委托能够在类A钦点义也得以在类A外定义。

     首先大家供给定义三个类来监听客户端事件,这里大家监听键盘的输入。

下边大家来为这一个事件赋值。

14.2.1.贰 、声美素佳儿个依据有些委托的风浪

Event delegateName  eventName;

eventName不是叁个体系,而是多少个切实的靶子,这么些具体的对象只幸亏类A钦点义而不可能在类A外定义。

     定义3个委托。

public void Init()
{   
    testEvent += new TestDelegate(EventSyntax_testEvent); 
    testEvent += EventSyntax_testEvent; 
}
private void EventSyntax_testEvent(string message)
{
    Console.WriteLine(message);
}
14.2.1.③ 、在类A中定义1个触发该事件的方法

ReturnType  FunctionName([parameters])

{

     ……

If(eventName != null)

{

eventName([parameters]);

或者eventName.Invoke([parameters]);

}

……

}

接触事件现在,事件所针对的函数将会被实施。那种实践是经过事件名称来调用的,就像是委托对象名相同的。

接触事件的不二法门只可以在A类中定义,事件的实例化,以及实例化之后的落到实处体都只可以在A类外定义。

         public delegate void UserRequest(object sender,EventArgs e);

如代码所示,我们采用了+=那个标记来为事件赋值,赋值的内容是1个委托和2个函数。

14.2.1.4、初始化A类的事件

在类B中定义2个类A的对象,并且让类A对象的丰富事件指向类B中定义的方法,那么些方法要与事件涉及的嘱托所界定的办法吻合。

    
后面的object用来传递事件的发生者,前边的伊芙ntArgs用来传递事件的细节,今后权且没什么用处,一会末尾的事例中将使用。

里面+=大家将她领悟为【添加】。

14.2.1.伍 、触发A类的轩然大波

在B类中去调用A类中的触发事件的法门:用A类的对象去调用A类的接触事件的措施。

     上边定义一个此委托项目类型的风云

代码中,我们使用三种赋值情势,但事实上都以为事件test伊夫nt添加3个委。

14.2.1.陆 、程序实例

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

using System.Windows.Forms;

namespace DelegateStudy

{

    public delegate void DelegateClick (int a);

    public class butt

    {

        public event DelegateClick Click;

        public void OnClick(int a)

        {

            if(Click != null)

                Click.Invoke(a);

               //Click(a);//那种方法也是足以的

            MessageBox.Show(“Click();”);

        }

    }

    class Frm

    {

        public static void Btn_Click(int a)

        {

            for (long i = 0; i < a; i++)

                Console.WriteLine(i.ToString());

        }

        static void Main(string[] args)

        {

            butt b = new butt();

        
  //在委托中,委托对象假如是null的,直接动用+=符号,会报错,不过在事件中,起初化的时候,只可以用+=

            b.Click
+= new DelegateClick (Fm_Click); //事件是依照委托的,所以委托估算一样适用,上面包车型大巴语句一样有效:b.Click
+= Fm_Click;

            //b.Click(10);错误:事件“DelegateStudy.butt.Click”只好冒出在
+= 或 -= 的左手(从品类“DelegateStudy.butt”中运用时除了)

            b.OnClick (10000);                       

            MessageBox.Show(“sd234234234”);

            Console.ReadLine();

        }

   }

}

         public event UserRequest OnUserRequest;

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

14.2.② 、控件事件

听别人说Windows的应用程序也是依照消息的。那注解,应用程序是经过Windows来与用户通讯的,Windows又是利用预订义的音信与应用程序通讯的。那几个新闻是富含种种消息的布局,应用程序和Windows使用这么些音信决定下一步的操作。

诸如:当用户用鼠标去点击二个windows应用程序的按钮的时候,windows操作系统就会捕获到那么些点击按钮的动作,那几个时候它会基于捕获到的动作发送一个与之相应的预约义的音讯给windows应用程序的那几个按钮,windows应用程序的按钮音信处理程序会处理接收到的音讯,那么些程序处理进度正是依照收到的新闻去接触相应的风浪,事件被按钮触发后,会打招呼全数的该事件的订阅者来接过那几个事件,从而执行相应的的函数。

在MFC等库或VB等花费环境推出以前,开发人士必须处理Windows发送给应用程序的音讯。VB和前些天的.NET把那个传送来的音信封装在事变中。假如急需响应某些音信,就应处理相应的风浪。

     下边我们来做八个死循环

系统提供事件

14.2.2.① 、控件事件委托伊芙ntHandler

在控件事件中,有广大的嘱托,在此处介绍八个最常用的委托伊芙ntHandler,.NET
Framework中央控制件的轩然大波很多都基于该信托,伊夫ntHandler委托已在.NET
Framework中定义了。它位于System命名空间:

Public delegate void EventHandler(object sender,EventArgs e);

         public void Run()
       {
       bool finished=false;
       do
       {
        if (Console.ReadLine()=="h")
        {
         OnUserRequest(this,new EventArgs());
        }  
       }while(!finished);
       }

C#的框架都很经典,而各样经典框架都为我们提供了有的经典事件。

14.2.2.② 、委托伊夫ntHandler参数和再次回到值

事件最终会指向3个要么七个函数,函数要与事件所遵照的嘱托匹配。事件所指向的函数(事件处理程序)的命名规则:依据约定,事件处理程序应遵照“object_event”的命名约定。object就是引发风浪的靶子,而event正是被吸引的轩然大波。从可读性来看,应依照那么些命名约定。

率先,事件处理程序连接回到void,事件处理程序不能够有重临值。其次是参数,只即便基于伊夫ntHandler委托的事件,事件处理程序的参数就应是object和伊芙ntArgs类型:

率先个参数接收引发事件的对象,比如当点击有个别按钮的时候,那么些按钮要接触单击事件最后实施这一个函数,那么就会把最近按钮传给sender,当有三个按钮的单击事件都指向那个函数的时候,sender的值就在于当前被单击的百般按钮,所以能够为多少个按钮定义二个按钮单击处理程序,接着依照sender参数明显单击了哪位按钮:

if(((Button)sender).Name ==”buttonOne”)

首个参数e是包涵关于事件的别样有用新闻的对象。

    
此代码不断的渴求用户输入字符,固然输入的结果是h,则触发OnUserRequest事件,事件的触发者是笔者(this),事件细节无(没有传递任何参数的伊芙ntArgs实例)。大家给那个类取名为UserInputMonitor。

是因为事件必须[标识响应措施的信托],所以那么些事件所选择的委托都有2个联手的特点,命名中含有伊夫nt。

14.2.2.三 、控件事件的此外委托

控件事件还有此外的寄托,比如在窗体上有与鼠标事件波及的委托:

Public delegate void MouseEventHandler(object sender,MouseEventArgs e);

public event MouseEventHandler MouseDown;

this.MouseDown
+= new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);

private void Form1_MouseDown(object sender, MouseEventArgs e){};

MouseDown事件采用MouseDown伊芙ntArgs,它富含鼠标的指针在窗体上的的X和Y坐标,以及与事件相关的其余消息。

控件事件中,一般第③个参数都以object
sender,第二个参数能够是专断档次,区别的信托能够有不一样的参数,只要它派生于伊夫ntArgs即可。

    下面我们要做的是定义客户端的类
     首先得实例化UserInputMonitor类

比如EventHandler,CancelEventHandler,RoutedEventHandler,ContextMenuEventHandler等。

14.2.2.四 、程序实例

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

 

namespace SecondChangeEvent1

{

    // 该类用来储存关于事件的有用新闻外,

    // 还用来存款和储蓄额外的要求传给订阅者的Clock状态消息

    public class TimeInfoEventArgs : EventArgs

    {

        public TimeInfoEventArgs(int hour,int minute,int second)

        {

            this.hour = hour;

            this.minute = minute;

            this.second = second;

        }

        public readonly int hour;

        public readonly int minute;

        public readonly int second;

    }

 

    // 定义名为SecondChangeHandler的信托,封装不重返值的主意,

    // 该办法带参数,多个clock类型对象参数,3个TimeInfoEventArgs类型对象

    public delegate void SecondChangeHandler(

       object clock,

       TimeInfoEventArgs timeInformation

    );

    // 被其余类观望的钟(Clock)类,该类公布1个风云:SecondChange。观看该类的类订阅了该事件。

    public class Clock

    {

        // 代表小时,分钟,秒的私家变量

        int _hour;

 

        public int Hour

        {

            get { return _hour; }

            set { _hour = value; }

        }

        private int _minute;

 

        public int Minute

        {

            get { return _minute; }

            set { _minute = value; }

        }

        private int _second;

 

        public int Second

        {

            get { return _second; }

            set { _second = value; }

        }

 

        // 要宣布的轩然大波

        public event SecondChangeHandler SecondChange;

 

        // 触发事件的不二法门

        protected void OnSecondChange(

           object clock,

           TimeInfoEventArgs timeInformation

        )

        {

            // Check if there are any Subscribers

            if (SecondChange != null)

            {

                // Call the Event

                SecondChange(clock, timeInformation);

            }

        }

 

        // 让钟(Clock)跑起来,每隔一分钟触发三回事件

        public void Run()

        {

            for (; ; )

            {

                // 让线程Sleep一秒钟

                Thread.Sleep(1000);

 

                // 获取当前时光

                System.DateTime dt = System.DateTime.Now;

 

                // 假使分钟变化了公告订阅者

                if (dt.Second != _second)

                {

                    // 创建TimeInfo伊夫ntArgs类型对象,传给订阅者

                    TimeInfoEventArgs timeInformation =

                       new TimeInfoEventArgs(

                       dt.Hour, dt.Minute, dt.Second);

 

                    // 布告订阅者

                    OnSecondChange(this, timeInformation);

                }

 

                // 更新景况消息

                _second = dt.Second;

                _minute = dt.Minute;

                _hour = dt.Hour;

 

            }

        }

    }

 

 

    /* ======================= Event Subscribers
=============================== */

 

    // 1个订阅者。DisplayClock订阅了clock类的轩然大波。它的干活是体现当前时间。

    public class DisplayClock

    {

        // 传入3个clock对象,订阅其SecondChangeHandler事件

        public void Subscribe(Clock theClock)

        {

            theClock.SecondChange +=

               new SecondChangeHandler(TimeHasChanged);

        }

 

        // 完毕了寄托匹配类型的法子

        public void TimeHasChanged(

           object theClock, TimeInfoEventArgs ti)

        {

 

            Console.WriteLine(“Current Time: {0}:{1}:{2}”,

               ti.hour.ToString(),

               ti.minute.ToString(),

               ti.second.ToString());

        }

    }

 

    // 第三个订阅者,他的行事是把当下日子写入3个文本

    public class LogClock

    {

        public void Subscribe(Clock theClock)

        {

            theClock.SecondChange +=

               new SecondChangeHandler(WriteLogEntry);

        }

 

        // 那么些措施自然应该是把音信写入1个文件中

        // 那里大家用把音讯输出控制台代替

        public void WriteLogEntry(

           object theClock, TimeInfoEventArgs ti)

        {

            Clock a = (Clock)theClock;

            Console.WriteLine(“Logging to file: {0}:{1}:{2}”,

               a.Hour.ToString(),

               a.Minute.ToString(),

               a.Second.ToString());

        }

    }

 

    /* ======================= Test Application
=============================== */

 

    // 测试拥有程序

    public class Test

    {

        public static void Main()

        {

            // 创建clock实例

            Clock theClock = new Clock();

 

            // 创制2个DisplayClock实例,让其订阅上面创制的clock的轩然大波

            DisplayClock dc = new DisplayClock();

            dc.Subscribe(theClock);

 

            // 创制三个LogClock实例,让其订阅上面创立的clock的事件

            LogClock lc = new LogClock();

            lc.Subscribe(theClock);

 

            // 让钟跑起来

            theClock.Run();

        }

    }

}

        UserInputMonitor monitor=new UserInputMonitor();

里头最经典的便是伊夫ntHandler和Routed伊芙ntHandler。

14. 3、小结

(1)、在概念事件的尤其类A里面,能够随心所欲的运用事件名,能够触发;在别的类里面,事件名只可以出现在
+= 或 -=
的左边来指向函数,即只好实例化,无法直接用事件名触发。不过能够通过A类的对象来调用A类中的触发事件的函数。那是唯一触发事件的点子。

(2)、不管是多播委托如故单播委托,在未曾异样处理的情形下,在叁个线程的执行进度中去调用委托(委托对象所针对的函数),调用委托的实施是不会新起线程的,那些执行可能在原线程中的,这几个对于事件也是均等的。当然,若是是在委托所针对的函数里面去运营三个新的线程那正是此外1次事了。

(3)、事件是指向某叁个实际的目的的,一般在该指标的所属类A中写好事件,并且写好触发事件的格局,那么这几个类A正是事件的宣布者,然后在别的类B里面定义A的目的,并去初阶化该指标的轩然大波,让事件指向B类中的某一个实际的办法,B类就是A类事件的订阅者。当通过A类的目的来触发A类的轩然大波的时候(只好A类的靶子来触发A类的事件,其余类的对象不可能触发A类的事件,只好订阅A类的风云,即实例化A类的风云),作为订阅者的B类会接收A类触发的轩然大波,从而使得订阅函数被执行。几个发表者能够有八个订阅者,当发表者发送事件的时候,全部的订阅者都将收受到事件,从而执行订阅函数,但是就算是有多少个订阅者也是单线程。

原著链接:
14.壹 、委托 当要把措施作为实…

     然后大家定义二个办法。

EventHandler:

       private void ShowMessage(object sender,EventArgs e)
       {
           Console.WriteLine(“HaHa!!”);
       }

伊夫ntHandler定义如下

     
最后要做的是把这几个办法和事件联系起来(订阅事件),大家把它写到库户端类的构造函数里。

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

     Client(UserInputMonitor m)
      {
       m.OnUserRequest+=new
UserInputMonitor.UserRequest(this.ShowMessage);
       //m.OnUserRequest+=new m.UserRequest(this.ShowMessage);

她带有了多个参数,即当大家为事件添加伊芙ntHandler委托后,再去触发该事件;被触发的嘱托将得到object
sender和伊夫ntArgs e四个参数。

       //注意那种写法是谬误的,因为委托是静态的

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

      }

e:代表事件参数,即触发该事件后,事件为被触发的委托,传递了有的参数,以便于委托在处理多少时,更简便易行。

      下边创立客户端的实例。

基于那一个规律,大家能够分析出众多东西。

        new Client(monitor);

比如说,当控件DataGrid的风浪被触发时,只要查看一下sender的真实性类型,就可以领略,到底是DataGrid触发的风浪,仍然DataGridRow或DataGridCell触发的了。

      对了,别忘了让monitor开端监听事件。

RoutedEventHandler:

         monitor.run();

Routed伊夫ntHandler即路由事件,他的概念如下

      马到功成,代码如下:

public delegate void RoutedEventHandler(
 Object sender,
 RoutedEventArgs e
)
using System;
class UserInputMonitor
{
 public delegate void UserRequest(object sender,EventArgs e);
 //定义委托
 public event UserRequest OnUserRequest;
 //此委托类型类型的事件
 public void Run()
 {
  bool finished=false;
  do
  {
   if (Console.ReadLine()=="h")
   {
    OnUserRequest(this,new EventArgs());
   }  
  }while(!finished);
 }
}

public class Client
{
 public static void Main()
 {
  UserInputMonitor monitor=new UserInputMonitor();
  new Client(monitor);
  monitor.Run();
 }
 private void ShowMessage(object sender,EventArgs e)
 {
  Console.WriteLine("HaHa!!");
 }
 Client(UserInputMonitor m)
 {
  m.OnUserRequest+=new UserInputMonitor.UserRequest(this.ShowMessage);
  //m.OnUserRequest+=new m.UserRequest(this.ShowMessage);
  //注意这种写法是错误的,因为委托是静态的
 }
}

Routed伊夫ntHandler也为大家提供了sender和e三个参数。

叁 、进一步钻探C#中的预订义事件处理机制

但Routed伊芙ntHandler尤其之处是,他的sender并不一定是实际的源,因为她是一个冒泡路由事件,即上涨事件。

     或者我们发现在C#中稍微事件和前边的仿佛不太雷同。例如

这边假设大家有好奇心去看官方文书档案,那么会在有关的牵线中看看三个单词sender和source。

       private void textBox1_KeyPress(object
sender, System.Windows.Forms.KeyPressEventArgs e)
       {

通过那四个单词,大家会清楚的询问路由事件。简单描述一下sender和source,它们3个是发送者,二个是源。

       }

在伊夫ntHandler中,sender即source,因为它是平昔事件。而在冒泡事件中,sender不一定等于source。即发送者不自然是源。

      
this.textBox1.KeyPress+=newSystem.Windows.Forms.KeyPressEventHandler(this.textBox1_KeyPress);

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

     那里运用了KeyPress伊芙ntArgs而不是伊芙ntArgs作为参数。那里运用了Key伊芙ntHandler委托,而不是伊芙ntHandler委托。

我们第3在XAML页面定义二个RadioButton按钮,然后设置他的沙盘是Button。然后分别定义各自的Click方法。

    KeyPress伊芙ntArgs是伊芙ntArgs的派生类,而Key伊芙ntHandler的注明如下

Xaml页面如下:

       public delegate void KeyEventHandler( object sender ,
KeyEventArgs e );

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

    是参数为Key伊夫ntArgs的嘱托。那干什么KeyPress事件要这么做吧,大家能够从多个类的构造函数来找答案。

cs文件事件如下:

       public EventArgs();

 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
 }

       public KeyPressEventArgs(char keyChar);

运转起来,大家点击按钮,通过断点我们能够见到,大家点击的按钮触发了btnClild_Click和btnParent_Click事件

     那里的keyData是什么样,是用来传递大家按下了哪个键的,哈。

逐一是先btnClild_Click后btnParent_Click。

     小编在Key伊芙ntArgs中又发现了品质

因此获得sender的花色,小编也足以见见,btnClild_Click的sender类型是Button,而btnParent_Click的sernder类型是RadioButton。

        public char KeyChar { get; }

事件驱动编制程序

     进一步表明了自个儿的答辩。上面我们来做三个好像的例子来接济理解。

事件驱动编程这一个定义给本身的痛感很怪,因为一贯用C#,而C#的很多框架都以事件驱动的,所以直接觉得事件驱动是自然。

四 、简单的自定义事件(2)

而当事件驱动设计这么些词平时出现后,反而感到奇怪。

    拿大家地点做的例子来改。

就就好像,每天吃黑米饭,突然有一天,全体人都说籼米饭好香的感觉到一样,你一听就感觉奇怪。

    
大家也定义一个伊芙ntArgs(类似Key伊芙ntArgs)取名My伊夫ntArgs,定义1个组织函数public
My伊夫ntArgs(char keyChar),同样大家也设置相应的习性。代码如下

因为事件驱动对于C#支出而言,实在太普通了。当然,那也得益于微软框架做的骨子里是太好了。

using System;
class MyMyEventArgs:EventArgs
{
 private char keyChar;
 public MyMyEventArgs(char keyChar)
 {
  this.keychar=keychar;
 }
 public char KeyChar
 {
  get
  {
   return keyChar;
  }
 }
}

所以,小编也不通晓哪些在C#里讲事件驱动编制程序。因为使用C#的框架正是选拔事件驱动编程。

因为未来要监听多少个键了,大家得改写监听器的类中的do…while部分。改写委托,改写客户端传递的参数。好了最后代码如下,好累

事件和嘱托到底是怎样关系?

using System;
class MyEventArgs:EventArgs
{
 private char keyChar;
 public MyEventArgs(char keyChar)
 {
  this.keyChar=keyChar;
 }
 public char KeyChar
 {
  get
  {
   return keyChar;
  }
 }
}

class UserInputMonitor
{
 public delegate void UserRequest(object sender,MyEventArgs e);
 //定义委托
 public event UserRequest OnUserRequest;
 //此委托类型类型的事件
 public void Run()
 {
  bool finished=false;
  do
  {
   string inputString= Console.ReadLine();
   if (inputString!="") 
    OnUserRequest(this,new MyEventArgs(inputString[0]));
  }while(!finished);
 }
}

public class Client
{
 public static void Main()
 {
  UserInputMonitor monitor=new UserInputMonitor();
  new Client(monitor);
  monitor.Run();
 }
 private void ShowMessage(object sender,MyEventArgs e)
 {
  Console.WriteLine("捕捉到:{0}",e.KeyChar);
 }
 Client(UserInputMonitor m)
 {
  m.OnUserRequest+=new UserInputMonitor.UserRequest(this.ShowMessage);
  //m.OnUserRequest+=new m.UserRequest(this.ShowMessage);
  //注意这种写法是错误的,因为委托是静态的
 }
}

事件是用来多播的,并且用委托来为事件赋值,能够说,事件是依照委托来兑现的。

但委托中也有多播,那干什么要独立弄出来二个风云呢?

率先,存在即创建,事件一定有她存在的意义。 

事件存在的含义

自个儿对事件存在的含义是这般敞亮的。我们在C#编写制定框架时,差不离不用委托的多播,因为委托的多播和事件存在严重的二义性。纵然编写框架的人学会了运用委托的多播,但运用框架的同事恐怕并还不太纯熟,而且C#框架中,大多是采纳事件来开展多播的。

之所以委托的多播和事件联合行使的框架,会招致选用那个框架的中低档开发者很多思疑,而那种猜疑,会时有发生不少不供给的难题。

比如说,
你定义了1个委托,另2个开发者用那几个委托做了个多播,当第5个开发者来保安那段代码时,如若她是新手,不驾驭委托的多播,那就很有恐怕只修改了信托调用的代码。而尚未去共同多播这一个委托的代码。这系统就产生了隐形的bug。

那正是说,事件和委托到底是哪些关系吧?

事件与信托的确存在复杂的涉及,怎么讲都以毋庸置疑的。但,C#开发者只须求记住,他们俩不要紧即可。在C#事件是事件,委托是信托。两者就就像是int和string一样,没有其它关系。

由来很粗大略,学习的进度中尽量降低概念混淆。而且,在C#开发中,好的架构者也司空见惯会将事件和委托分离,所以,就以为事件和信托没有涉嫌即可。

结语

实质上事件很好精晓,一点不复杂。笔者在写那篇作品的历程中,也没悟出什么尤其的只怕说比较高级的用法。

但真正的运用场景中,作者的感到是,随着MVVM的成材,事件实际在被日益丢弃。固然微软做了不少经文的事件驱动框架。但这都以病故了。

例如WPF就算扶助事件驱动,但MVVM在WPF下的展现堪称完美,所以WPF下的轩然大波差不多一贯不人用了。

再譬如前端的Angularjs等框架,提供了上流的MVVM使用功效,也让新的前端设计师慢慢遗弃了事件。

由此,事件在今后的编制程序中,很大概将不在有那么重庆大学的身份了。但学好事件,对于大家理解微软框架,依然有十分的大帮扶的。

C#语法——元组类型

C#语法——泛型的各种运用

C#语法——await与async的不错打开药方式

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

我对C#的认知。


注:此小说为原创,欢迎转载,请在小说页面分明地点给出此文链接!
若您觉得那篇作品还不易,请点击下右下角的【推荐】,非凡多谢!
一经你认为那篇小说对您抱有匡助,这就无妨支付宝小小打赏一下啊。 

亚洲必赢官网 4

 

网站地图xml地图