浅显WPF,慢慢边缘化的二哥

事件是C#的基本功之一,学好事件对于通晓.NET框架大有益处。

  如果对事件一点都不驾驭照旧是拖泥带水的话,建议先去看张子阳的寄托与事件的篇章(相比较长,或然看完了,也记不清看这一篇了,没事,作者会原谅你的),废话不多说,初叶进入正题。本记录不是记录古板的轩然大波(CL路虎极光事件),而是记录WPF中常用的事件——路由事件,由于路由事件“传播”的时间是沿着可视树传播的,所以在记录开端在此之前依然明白一下逻辑树和可视树。

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

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

事件最广泛的比喻正是订阅,即,假使你订阅了自身的博客,那么,当笔者发表新博客的时候,你就会赢得关照。

 ① 、逻辑树和可视树

  在WPF中有二种树:逻辑树(Logical Tree)和可视树(Visual
Tree),XAML是表明WPF的一棵树。逻辑树完全是由布局组件和控件构成。倘诺大家把逻辑树延伸至Template组件级别,大家就拿走了可视树,所以可视树把树分的更仔细。由于本记录重在笔录事件,所以不做过多公布逻辑树和可视树的内容。关于逻辑树和可视树的区分能够参照。

    
在写代码前大家先来熟练.net框架中和事件有关的类和委托,精通C#中预订义事件的处理。

原作链接:

而这么些历程正是事件,也许说是事件运营的轨迹。

贰 、路由事件

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

14.1、委托

当要把办法作为实参传送给其余措施的形参时,形参供给采纳委托。委托是2个类型,是贰个函数指针类型,那个类型将该信托的实例化对象所能指向的函数的细节封装起来了,即分明了所能指向的函数的签订契约,也等于限制了所能指向的函数的参数和重返值。当实例化委托的时候,委托对象会针对某三个一双两好的函数,实质便是将函数的地方赋值给了该信托的指标,然后就足以经过该信托对象来调用所针对的函数了。利用委托,程序员能够在委托对象中封装2个艺术的引用,然后委托对象作为形参将被传给调用了被引用方法的代码,而不需求精晓在编写翻译时刻具体是哪些方法被调用。

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

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

2.① 、小记事件

  假若看完了信托与事件的稿子,相信会对事件有更进一步的认识了,但依然要把一部分基础的地点再记录一下。3个事件要有上边多少个因素,才会变的有含义:

    • 事件的拥有者(sender)——即音讯的发送者。
    • 事件发送的音讯(伊芙ntAgs)
    • 事件的响应者——新闻的接收者(对象)。
    • 响应者的处理器——音讯的接受者要对新闻作出处理的艺术(方法名)。
    • 响应者对发送者事件的订阅

     EventHandler是二个寄托证明如下

14.1.① 、定义委托

语法如下

delegate  result-type   Identifier ([parameters]);

说明:

result-type:重临值的品类,和章程的回到值类型一致

Identifier:委托的称谓

parameters:参数,要引用的办法带的参数

小结

当定义了寄托随后,该信托的对象自然能够而且也只可以指向该信托所界定的函数。即参数的个数、类型、顺序都要合作,重回值的花色也要合作。

因为定义委托相当于是定义三个新类,所以能够在定义类的其他地点定义委托,既能够在一个类的内部定义,那么此时即将通过此类的类名来调用那几个委托(委托必须是public、internal),也得以在其余类的外部定义,那么此时在命名空间中与类的级别是如出一辙的。依照定义的可知性,可以在信托定义上添加一般的拜访修饰符:当委托定义在类的外围,那么能够添加public、internal修饰符;假若委托定义到类的内部,那么可以加上public、 private、 protected、internal。一般委托都以概念在类的外界的。

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

2.2 初试路由事件

  大家建立2个winform项目,然后在窗体上添加二个按钮,双击添加贰个处理器,会发觉private
void btn_Click(object sender, 伊芙ntArgs
e)处理器要拍卖新闻是伊夫ntArgs类型的,那里对应的是观念的风云(大家叫它CL卡宴事件)。同样大家树立1个WPF项目,然后添加多个按钮,双击添加二个电脑,会发现private
void Button_Click(object sender, RoutedEventArgs
e)处理器要处理的新闻是Routed伊夫ntArgs类型的,那里对应的是路由事件。两者有如何分别呢。让大家先看看CLEscort事件的弊端,就如(this.btn.Click
+= new
System.伊芙ntHandler(this.btn_Click);)每三个消息都是从发送到响应的三个历程,当1个总结机要用多次,必须建立显式的点对点订阅关系(窗体对按钮事件的订阅,如若是再有三个按钮的话,就要再来三遍订阅);还有三个弊病是:事件的宿主必须能够一向访问事件的响应者,不然不可能树立订阅关系(如有多个零部件,点击组件一的按钮,想让组件二响应事件,那么就让组件二向组件一的按钮暴光三个方可访问的事件,那样一旦再多多少个嵌套,会油但是惹祸件链,有揭露假若暴光不当就存在着吓唬)。路由事件除了能很好的消除地点的难题,还有3个是路由事件在有路的气象下,能很好的依据规定的法子传播事件,因为XAML的树状结构,构成了一条条的征途,所以在WPF中,引入了路由事件。举个例子:倘若窗体要以相同的章程处理五个按钮的风云,大家就能够用一句代码就解决了,this.AddHandler(Button.Click伊芙nt,
new
Routed伊夫ntHandler(this.ButtonClicked));那样就收缩代码量。上面通过1个例子初试一下路由事件。 给出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的分别。由于音讯每传一站,都要把音讯交个三个控件(此控件成为了新闻的出殡地点),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

 

          public delegate void EventHandler( object sender , EventArgs
e )

14.1.二 、实例化委托

Identifier  objectName  =  new  Identifier( functionName);

实例化委托的本质正是将某些函数的地方赋值给委托对象。在此间:

Identifier :那个是寄托名字。

objectName :委托的实例化对象。

functionName:是该信托对象所指向的函数的名字。对于那几个函数名要尤其注意:定义这么些委托对象自然是在类中定义的,那么一旦所指向的函数也在此类中,不管该函数是静态仍然非静态的,那么就径直写函数名字就能够了;假设函数是在其他类里面定义的public、

internal,可是倘就算静态,那么就直接用类名.函数名,假诺是非静态的,那么就类的靶子名.函数名,那个函数名与该对象是有关联的,比如如若函数中冒出了this,表示的就是对脚下指标的调用。

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

 2.3自定义路由事件

  通过上面的小规模试制路由事件,应该适中由事件某个领悟了,上面就记录一下怎么自定义3个路由事件。大致分为八个步骤:1.扬言并注册路由事件,2.为路由事件添加CLCRUISER事件包装,3.创造能够激发路由事件的艺术。纪念一下依赖属性,前五个步骤应该和路由事件很相似吧。上边将八个步骤分开来证实:

率先步:注脚并注册路由事件       

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

其次步:为路由事件添加CL奥迪Q5事件包装

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

  注意上边的一条龙代码,在注脚和定义路由事件时,第④个参数,委托的门类和处理器方法的的参数一定要一如既往,不然会报错,能够把上边一句中的ReportTimeRoute伊芙ntHandler换来Routed伊夫ntHandler试试,会出现:不能从文本“ReportTimeHandler”创设“ReportTime”。”,行号为“5”,行职务为“30”,的难题,那里首要的来由正是委托的参数和Routed伊芙ntHandler的参数差异,即便都是(sender, 
e);但是e的项目已经发生了转变,成为了ReportTime伊夫ntArgs类型的。所以在动用此前,声爱他美(Karicare)个委托就足以了。还有个法子是行使伊芙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;

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

14.1.三 、委托猜测

C#
2.0用委托推断扩大了委托的语法。当大家要求定义委托对象并实例化委托的时候,就可以只传送函数的名目,即函数的地址:

Identifier  objectName  =  functionName;

那个中的functionName与14.1.2节中实例化委托的functionName是平等的,没什么区别,满意上边的条条框框。

C#编写翻译器创设的代码是同样的。编写翻译器会用objectName检查和测试供给的信托项目,因此会成立Identifier委托类型的二个实例,用functionName即方法的地址传送给Identifier的构造函数。

注意:

无法在functionName前面加括号和实参,然后把它传送给委托变量。调用方法一般会回到3个不能够给予委托变量的一般对象,除非这一个措施重返的是3个相当的信托对象。总之:只好把相匹配的艺术的地方赋予委托变量。

委托估量能够在须求委托实例化的别的市方使用,就跟定义普通的寄托对象是相同的。委托预计也能够用来事件,因为事件基于委托(参见本章前边的始末)。

事件的概念

 三、总结

  本篇记录的剧情纵然不多,但是感觉记录的年华专程讨厌,首借使因为对事件的多少个组成都部队分还不是万分谙习,而且在知情路由事件时,还要先清楚逻辑树与可视树。最后依旧把这一章看完了,但这些只是始于。

  小说首要记录了路由事件的在可视树上的流传以及自定义路由事件的达成。若是在小说有两样的理念或提议,欢迎交换! 下一篇:《深切浅出WPF》笔记——命令篇

 

    
下边大家商量一下Button类看看里面包车型大巴轩然大波申明(使用WinCV工具查看),以Click事件为例。

14.1.四 、匿超形式

到近日停止,要想使委托工作,方法必须已经存在。但实例化委托还有其它一种办法:即透过匿名格局。

用匿名方式定义委托的语法与前边的定义并不曾差异。但在实例化委托时,就有分别了。上面是3个11分简单的控制台应用程序,表达了什么样利用匿名方式:

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)

      {

        param += mid;

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

        return param;

      };

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

    }

  }

}

信托DelegateTest在类Program中定义,它带贰个字符串参数。有分别的是Main方法。在定义anonDel时,不是传递已知的措施名,而是使用叁个简练的代码块:它前面是根本字delegate,后边是2个参数:

delegate(string param)

{

  param += mid;

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

  return param;

};

匿名格局的亮点是削减了要编写的代码。方法仅在有嘱托行使时才定义。在为事件定义委托时,这是非常鲜明的。(本章前面研究事件。)那促进下降代码的复杂性,尤其是概念了少数个事件时,代码会显得比较简单。使用匿名格局时,代码执行得不太快。编写翻译器仍定义了3个措施,该格局惟有3个机关钦赐的称呼,我们不必要驾驭这几个称谓。

在行使匿名情势时,必须比照多少个规则:

壹 、在匿超情势中不可能应用跳转语句跳到该匿名方式的外表,反之亦然:匿名格局外部的跳转语句不可能跳到该匿名格局的里边。

二 、在匿名格局内部不能够访问不安全的代码。别的,也无法访问在匿名格局外部使用的ref和out参数。但能够运用在匿名格局外部定义的别样变量。方法内部的变量、方法的参数可以轻易的接纳。

若是需求用匿超情势多次编纂同二个意义,就无须选拔匿名格局。而编辑1个钦点的法子相比好,因为该措施只需编写三次,未来可由此名称引用它。

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

          public event EventHandler Click;

14.1.五 、多播委托

亚洲必赢手机入口,前方使用的种种委托都只包涵一个措施调用,调用委托的次数与调用方法的次数相同,就算要调用多少个方式,就须求反复给委托赋值,然后调用那几个委托。

信托也得以分包多少个章程,那时候要向委托对象中添加多少个措施,那种委托称为多播委托,多播委托有一个主意列表,借使调用多播委托,就足以两次三番调用四个艺术,即先实施某二个格局,等该格局执行到位现在再履行此外三个办法,这么些方法的参数都以同一的,那一个办法的推行是在1个线程中履行的,而不是种种方法皆以2个线程,最终将推行到位全部的办法。

一旦利用多播委托,就应留神对同2个委托调用方法链的逐条并未正式定义,调用顺序是不显明的,不肯定是规行矩步添加艺术的顺序来调用方法,因而应避免编写制定注重于以特定顺序调用方法的代码。假若要想分明顺序,那么只好是单播委托,调用委托的次数与调用方法的次数相同。

多播委托的一一艺术签名最好是回去void;不然,就只好获取委托最终调用的二个主意的结果,而结尾调用哪个方法是无力回天分明的。

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

自个儿要好写代码测试,测试的结果日前都是调用顺序和进入委托的各样相同的,不过不拔除有例外的时候。 

delegate result-type Identifier ([parameters]); 

沟通平常语言便是,事件能够定义成静态的或一般的,所以事件就可以由表明的对象调用,也得以平昔通过类调用静态事件。

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

14.1.5.壹 、委托运算符 =

Identifier  objectName  =  new  Identifier( functionName);

或者

Identifier  objectName  =  functionName;

此地的“=”号表示清空 objectName 的不二法门列表,然后将 functionName 参预到     objectName 的情势列表中。

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

    
后面包车型客车剧情都以C#在类库中早就为我们定义好了的。下边大家来看编制程序时产生的代码。

14.1.5.二 、委托运算符 +=

objectName  +=  new  Identifier( functionName1);

或者

objectName  +=  functionName1;

此间的“+=”号表示在原始的法子列表不变的情景下,将 functionName1  出席到     objectName 的点子列表中。能够在措施列表中丰硕多少个一样的法门,执行的时候也会执行完全体的函数,哪怕有同一的,就会频仍实施同1个艺术。

留意:objectName 必须是一度赋值了的,不然在概念的时候一向动用该符号:

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

Identifier  objectName  +=  functionName1;就会报错。

下边大家来看最基础的事件定义。

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

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

objectName  -=  new  Identifier( functionName1);

或者

objectName  -=  functionName1;

那边的“-=”号表示在 objectName 的措施列表中减去1个functionName1。能够在方式列表中多次减去划一的格局,减1回只会减一个格局,假使列表中无此措施,那么减就不曾意思,对原始列表无影响,也不会报错。

瞩目:objectName 必须是曾经赋值了的,不然在概念的时候向来选用该符号:

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

Identifier  objectName  -=  functionName1;就会报错。

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

    
那是我们和button1_click事件所对应的艺术。注意方法的参数符合委托中的签名(既参数列表)。那大家怎么把那一个办法和事件联系起来吧,请看上边包车型客车代码。

14.1.5.④ 、委托运算符 +、-:

Identifier  objectName  =  objectName  + functionName1 –
functionName1;或者

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

对此那种+、-表明式,在第②个记号+只怕-的前头必须是信托而不可能是格局,前面包车型地铁+、-左右都不管。以此不是纯属规律,还有待进一步的研究。

小编们第②定义了八个寄托,然后使用event关键字,定义3个事变。

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

14.1.5.伍 、多播委托的越发处理

由此3个委托调用三个法子还有一个大难题。多播委托包涵二个挨家挨户调用的信托集合。如若经过信托调用的2个主意抛出了十分,整个迭代就会终止。上边是MulticastIteration示例。个中定义了七个简便的委托德姆oDelegate,它从不参数,重返void。那些委托调用方法One()和Two(),那七个措施满意委托的参数和重回类型要求。注意方法One()抛出了三个可怜:

浅显WPF,慢慢边缘化的二哥。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()方法的地址添加到同3个寄托中。调用d1寄托,就能够调用那五个主意。卓殊在try/catch块中抓获:

static void Main()

{

DemoDelegate d1 = One;

d1 += Two;

try

{

d1();

}

catch (Exception)

{

Console.WriteLine(“Exception caught”);

}

}

}

}

信托只调用了第一个办法。第多个办法抛出了要命,所以委托的迭代会停止,不再调用Two()方法。当调用方法的次第没有点名时,结果会有所不一致。

One

Exception Caught

注意:

多播委托包括贰个各类调用的信托集合。假使通过信托调用的3个艺术抛出了充足,整个迭代就会终止。即只要任一方法引发了那些,而在该措施内未捕获该尤其,则该越发将传递给委托的调用方,并且不再对调用列表中前面包车型客车方法实行调用。

在这种情形下,为了防止这些难题,应手动迭代方法列表。Delegate类定义了艺术GetInvocationList(),它回到二个Delegate对象数组。今后能够运用这几个委托调用与寄托直接相关的法子,捕获分外,并持续下三次迭代。

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”);

}

}

}

修改了代码后运营应用程序,会看到在抓获了那多少个后,将继续迭代下三个办法。

One

Exception caught

Two

留神:其实倘使在多播委托的每种具体的章程中捕获至极,并在内处,而不抛出十一分,一样能完结多播委托的富有办法执行完毕。那种措施与地点情势的界别在于那种格局的襄阳市在函数内处的,下面那种情势的百般是在函数外面捕获并处理的。

全部上看,好像正是在概念叁个寄托,只是在委托的概念在此之前,加了个event关键字。

     把this.button1_Click方法绑定到this.button1.Click轩然大波。

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

一 、委托实例的名称,前面包车型大巴括号中应涵盖调用该信托中的方法时行使的参数。

二 、调用委托对象的Invoke()方法,Invoke前面包车型大巴括号中应涵盖调用该信托中的方法时选拔的参数。

瞩目:实际上,给委托实例提供括号与调用委托类的Invoke()方法完全相同。因为Invoke()方法是信托的一路调用方法。

 

注意:不管是多播委托依旧单播委托,在一直不非凡处理的情景下,在三个线程的推行进程中去调用委托(委托对象所针对的函数),调用委托的施行是不会新起线程的,这几个执行只怕在原线程中的,那些对于事件也是相同的。当然,若是是在信托所指向的函数里面去运行3个新的线程那就是此外贰次事了。

科学,事件的定义正是这么,因为要声美赞臣个风浪,需求五个要素:

    
上边大家研商一下C#事件处理的干活流程,首先系统会在为大家创制2个在后台监听事件的靶子(假设是
button1的事件那么监听事件的正是button1),这些目标用来发出事件,假如有某些用户事件发生则发出相应的应用程序事件,然后实施订阅了风浪的全体办法。

14.2、事件

一,标识提供对事件的响应的措施的寄托。

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

14.2.壹 、自定义事件

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

     首先我们要求定义一个类来监听客户端事件,这里大家监听键盘的输入。

14.2.1.一 、声飞鹤个委托:

Delegate result-type delegateName ([parameters]);

以此委托能够在类A钦定义也得以在类A外定义。

下边大家来为那么些事件赋值。

     定义一个信托。

14.2.1.贰 、声美素佳儿(Friso)个依照有个别委托的风浪

Event delegateName  eventName;

eventName不是2个种类,而是一个切实可行的靶子,这些实际的对象只还好类A钦定义而不可能在类A外定义。

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

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

14.2.1.叁 、在类A中定义贰个触发该事件的点子

ReturnType  FunctionName([parameters])

{

     ……

If(eventName != null)

{

eventName([parameters]);

或者eventName.Invoke([parameters]);

}

……

}

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

接触事件的点子只万幸A类中定义,事件的实例化,以及实例化之后的落到实处体都只幸好A类外定义。

如代码所示,大家运用了+=那一个标记来为事件赋值,赋值的内容是3个委托和一个函数。

    
前边的object用来传递事件的爆发者,前边的伊夫ntArgs用来传递事件的底细,以后近来没什么用处,一会背后的例子中校使用。

14.2.1.四 、伊始化A类的轩然大波

在类B中定义二个类A的对象,并且让类A对象的不得了事件指向类B中定义的办法,那么些方法要与事件波及的嘱托所界定的主意吻合。

内部+=大家将他领略为【添加】。

     下边定义3个此委托项目类型的轩然大波

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

在B类中去调用A类中的触发事件的艺术:用A类的目的去调用A类的触及事件的情势。

代码中,大家选取二种赋值模式,但骨子里都以为事件testEvent添加二个委。

         public event UserRequest OnUserRequest;

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();

        }

   }

}

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

     下边大家来做三个死循环

14.2.贰 、控件事件

据他们说Windows的应用程序也是基于新闻的。这注解,应用程序是通过Windows来与用户通讯的,Windows又是选用预约义的音讯与应用程序通讯的。那几个新闻是富含各样新闻的结构,应用程序和Windows使用那些音信决定下一步的操作。

比如:当用户用鼠标去点击3个windows应用程序的按钮的时候,windows操作系统就会捕获到这些点击按钮的动作,那些时候它会依据捕获到的动作发送五个与之对应的预定义的新闻给windows应用程序的那几个按钮,windows应用程序的按钮新闻处理程序会处理接收到的新闻,那么些程序处理进程便是基于收到的新闻去接触相应的事件,事件被按钮触发后,会通报全数的该事件的订阅者来接过这么些事件,从而执行相应的的函数。

在MFC等库或VB等开发条件推出在此之前,开发职员必须处理Windows发送给应用程序的新闻。VB和明日的.NET把这一个传送来的消息封装在事变中。要是急需响应有个别新闻,就应处理相应的风浪。

系统提供事件

         public void Run()
       {
       bool finished=false;
       do
       {
        if (Console.ReadLine()=="h")
        {
         OnUserRequest(this,new EventArgs());
        }  
       }while(!finished);
       }
14.2.2.一 、控件事件委托伊芙ntHandler

在控件事件中,有不少的嘱托,在此地介绍一个最常用的委托伊芙ntHandler,.NET
Framework中央控制件的轩然大波很多都依据该信托,伊夫ntHandler委托已在.NET
Framework中定义了。它坐落System命名空间:

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

C#的框架都很经典,而各种经典框架都为大家提供了有的经文事件。

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

14.2.2.二 、委托伊芙ntHandler参数和再次来到值

事件结尾会针对三个还是多个函数,函数要与事件所依照的信托匹配。事件所指向的函数(事件处理程序)的命名规则:依照预定,事件处理程序应遵循“object_event”的命名约定。object便是诱惑风云的靶子,而event正是被掀起的事件。从可读性来看,应遵照那些命名约定。

率先,事件处理程序连接回到void,事件处理程序不可能有重返值。其次是参数,只尽管基于伊芙ntHandler委托的轩然大波,事件处理程序的参数就应是object和伊芙ntArgs类型:

先是个参数接收引发事件的目的,比如当点击有些按钮的时候,那一个按钮要接触单击事件结尾实施那些函数,那么就会把当前按钮传给sender,当有多少个按钮的单击事件都指向那些函数的时候,sender的值就在于当前被单击的十三分按钮,所以能够为多少个按钮定义二个按钮单击处理程序,接着依据sender参数显明单击了哪位按钮:

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

第二个参数e是含有关于事件的其余有用新闻的靶子。

出于事件必须[标识响应措施的嘱托],所以那些事件所采纳的委托都有2个齐声的性状,命名中隐含伊芙nt。

    下面我们要做的是定义客户端的类
     首先得实例化UserInputMonitor类
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即可。

比如EventHandler,CancelEventHandler,RoutedEventHandler,ContextMenuEventHandler等。

        UserInputMonitor monitor=new UserInputMonitor();

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的委托,封装不重返值的主意,

    // 该格局带参数,2个clock类型对象参数,多少个TimeInfo伊夫ntArgs类型对象

    public delegate void SecondChangeHandler(

       object clock,

       TimeInfoEventArgs timeInformation

    );

    // 被其余类观察的钟(Clock)类,该类公布3个事变: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
=============================== */

 

    // 一个订阅者。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());

        }

    }

 

    // 第三个订阅者,他的干活是把当下岁月写入三个文件

    public class LogClock

    {

        public void Subscribe(Clock theClock)

        {

            theClock.SecondChange +=

               new SecondChangeHandler(WriteLogEntry);

        }

 

        // 那些主意自然应该是把音讯写入贰个文书中

        // 那里大家用把新闻输出控制台代替

        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();

 

            // 创制1个DisplayClock实例,让其订阅上边创立的clock的轩然大波

            DisplayClock dc = new DisplayClock();

            dc.Subscribe(theClock);

 

            // 创立一个LogClock实例,让其订阅下边创造的clock的风云

            LogClock lc = new LogClock();

            lc.Subscribe(theClock);

 

            // 让钟跑起来

            theClock.Run();

        }

    }

}

中间最经典的便是伊夫ntHandler和Routed伊夫ntHandler。

     然后大家定义一个措施。

14. 3、小结

(1)、在概念事件的不行类A里面,可以肆意的施用事件名,能够触发;在其余类里面,事件名只可以出现在
+= 或 -=
的左手来指向函数,即只可以实例化,不能够平素用事件名触发。不过能够透过A类的指标来调用A类中的触发事件的函数。那是唯一触发事件的不二法门。

(2)、不管是多播委托依旧单播委托,在未曾新鲜处理的情事下,在二个线程的执行进度中去调用委托(委托对象所指向的函数),调用委托的实践是不会新起线程的,那么些执行恐怕在原线程中的,那么些对于事件也是一致的。当然,若是是在委托所针对的函数里面去运维三个新的线程那就是其它贰遍事了。

(3)、事件是指向某2个有血有肉的指标的,一般在该目的的所属类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#中的预订义事件处理机制

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

     恐怕大家发今后C#中有个别事件和后面包车型地铁就如不太一样。例如

此地假设大家有好奇心去看官方文档,那么会在连锁的牵线中看到五个单词sender和source。

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

通过那多个单词,我们会清楚的刺探路由事件。简单描述一下sender和source,它们一个是发送者,一个是源。

       }

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

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

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

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

咱俩首先在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,定义2个构造函数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);
  //注意这种写法是错误的,因为委托是静态的
 }
}

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

但委托中也有多播,那为啥要独自弄出来1个事件呢?

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

事件存在的意义

自笔者对事件存在的含义是这么敞亮的。大家在C#编纂框架时,大概不用委托的多播,因为委托的多播和事件存在严重的二义性。尽管编写框架的人学会了运用委托的多播,但使用框架的同事可能并还不太熟稔,而且C#框架中,大多是利用事件来进行多播的。

由此委托的多播和事件联合利用的框架,会招致选择这几个框架的低档开发者很多质疑,而那种疑心,会发生众多不必要的标题。

例如,
你定义了三个信托,另2个开发者用这一个委托做了个多播,当第八个开发者来保卫安全那段代码时,如若她是新手,不打听委托的多播,那就很有大概只修改了委托调用的代码。而尚未去一起多播这些委托的代码。那系统就时有发生了隐形的bug。

那么,事件和寄托到底是何许关系吗?

事件与寄托的确存在复杂的涉嫌,怎么讲都以正确的。但,C#开发者只须要牢记,他们俩没关系即可。在C#事件是事件,委托是信托。两者就像是同int和string一样,没有其它涉及。

原因很简单,学习的进程中尽量下降概念混淆。而且,在C#支出中,好的框架结构者也不足为怪会将事件和信托分离,所以,就觉着事件和寄托没有提到即可。

结语

其实事件很好精通,一点不复杂。小编在写那篇文章的过程中,也没悟出什么尤其的恐怕说比较高档的用法。

但真正的施用场景中,笔者的感觉是,随着MVVM的成人,事件实际上在被逐级遗弃。纵然微软做了不可胜举经典的事件驱动框架。但那都以过去了。

譬如WPF尽管补助事件驱动,但MVVM在WPF下的变现堪称完美,所以WPF下的风云大约从未人用了。

再譬如前端的Angularjs等框架,提供了上品的MVVM使用效益,也让新的前端设计师渐渐废弃了风浪。

故而,事件在今后的编制程序中,相当的大概将不在有那么主要的身价了。但学好事件,对于大家领略微软框架,依旧有非常的大协助的。

C#语法——元组类型

C#语法——泛型的两种利用

C#语法——await与async的正确打开药形式

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

我对C#的认知。


注:此小说为原创,欢迎转发,请在篇章页面分明地方给出此文链接!
若你觉得那篇小说还不易,请点击下右下角的【推荐】,相当多谢!
借使您认为那篇作品对你有所援助,那就不妨支付宝小小打赏一下啊。 

亚洲必赢手机入口 4

 

发表评论

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

网站地图xml地图