您的位置:澳门新葡8455最新网站 > 编程教学 > 逐渐边缘化的大哥,深入浅出WPF

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

发布时间:2019-12-13 19:07编辑:编程教学浏览(187)

    事件是C#的底工之生机勃勃,学好事件对于领悟.NET框架大有益处。

      要是对事件一点都不打听依旧是拖泥带水的话,提议先去看张子阳的寄托与事件的稿子(相比较长,或然看完了,也忘记看那后生可畏篇了,没事,小编会原谅你的),废话非常少说,最初步向正题。本记录不是记录古板的事件(CLLX570事件),而是记录WPF中常用的风浪——路由事件,由于路由事件“传播”的时刻是沿着可视树传播的,所以在记录最早以前还是询问一下逻辑树和可视树。

    事件最布满的譬喻就是订阅,即,要是您订阅了自家的博客,那么,当自身揭橥新博客的时候,你就能够赢得照望。

     风流浪漫、逻辑树和可视树

      在WPF中有三种树:逻辑树(Logical Tree)和可视树(Visual Tree),XAML是揭橥WPF的生龙活虎棵树。逻辑树完全都以由布局组件和控件构成。若是大家把逻辑树延伸至Template组件等第,大家就拿走了可视树,所以可视树把树分的越来越细致。由于本记录重在笔录事件,所以不做过多发表逻辑树和可视树的剧情。关于逻辑树和可视树的界别能够参照他事他说加以考察。

    而以此进程正是事件,或然说是事件运维的轨迹。

    二、路由事件

    事件是散落,以自己的博客为着力,向具备订阅者发送音信。大家把这种分散称之为[多播]。

    2.1、小记事件

      如若看完了寄托与事件的小说,相信会对事件有更进一层的认识了,但要么要把部分根基之处再记录一下。多个事变要有下边几个因素,才会变的有意义:

      • 事件的具备者(sender)——即音讯的发送者。
      • 事件发送的音讯(EventAgs)
      • 事件的响应者——消息的收信人(对象)。
      • 响应者的计算机——音讯的采取者要对新闻作出管理的诀要(方法名)。
      • 响应者对发送者事件的订阅

    最不足为道的事件用处是窗体编制程序,在Windows窗体应用程序和WPF应用程序中。

    2.2 初试路由事件

      我们创立四个winform项目,然后在窗体上增添一个开关,双击增多三个微型机,会意识private void btn_Click(object sender, 伊夫ntArgs e卡塔尔微型机要拍卖消息是伊芙ntArgs类型的,这里对应的是古板的轩然大波(我们叫它CL瑞鹰事件)。相似大家组建一个WPF项目,然后增添一个开关,双击增添五个Computer,会意识private void Button_Click(object sender, RoutedEventArgs e卡塔尔微处理机要管理的消息是Routed伊芙ntArgs类型的,这里对应的是路由事件。两个有怎么样界别吧。让大家先看看CLTucson事件的坏处,就好像(this.btn.Click += new System.EventHandler(this.btn_Click卡塔尔(قطر‎;)每二个音讯都以从发送到响应的叁个进度,当多个Computer要用数次,必得建构显式的点对点订阅关系(窗体对开关事件的订阅,要是是再有二个开关的话,将在再来贰回订阅);还只怕有多少个害处是:事件的宿主必需能够直接访谈事件的响应者,不然不可能树立订阅关系(如有七个零件,点击组件生机勃勃的开关,想让组件二响应事件,那么就让组件二向组件生机勃勃的开关拆穿贰个得以访谈的平地风波,那样只要再多多少个嵌套,会合世事件链,有暴光假若暴光不当就存在着压迫)。路由事件除了能很好的消除地点的难点,还或然有三个是路由事件在有路的意况下,能很好的遵照规定的章程传播事件,因为XAML的树状构造,构成了一条条的征程,所以在WPF中,引进了路由事件。举例:要是窗体要以相似的不二等秘书技管理五个开关的风浪,我们就能够用一句代码就消除了,this.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.ButtonClicked卡塔尔(قطر‎卡塔尔(英语:State of Qatar);那样就收缩代码量。下边通过一个例子初试一下路由事件。 给出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卡塔尔(英语:State of Qatar)卡塔尔(قطر‎;订阅事件时,第贰个参数是路由事件类型,在那地用的是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.为路由事件增多CLOdyssey事件包装,3.创造能够激情路由事件的诀要。回想一下依附属性,前四个步骤应该和路由事件很相似吧。上边将多少个步骤分开来验证:

    第一步:注明并注册路由事件       

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

    第二步:为路由事件增加CLOdyssey事件包装

           /*包装事件
            *这里与传统的数据差别是把+=和-=换成了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卡塔尔(英语:State of Qatar);可是e的花色已经发出了变动,成为了ReportTimeEventArgs类型的。所以在利用以前,声Bellamy(Bellamy卡塔尔(英语:State of Qatar)个委托就足以了。还恐怕有个法子是运用EventHandler<ReportTime伊夫ntArgs>替换ReportTimeRouteEventHandler,其实相互的用法差不离,只是不一样的写法,但是是本身备感第后生可畏种写法会越来越好掌握。具体有关伊夫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;
    

    事件的定义

     三、总结

      本篇记录的内容即使异常少,不过认为记录的小时特意讨厌,重假若因为对事件的几个组成都部队分还不是不行熟谙,何况在领会路由事件时,还要先清楚逻辑树与可视树。最后还是把那风流倜傥章看完了,但以此只是从头。

      随笔首要记录了路由事件的在可视树上的传遍以致自定义路由事件的兑现。即使在小说有差别的理念或提出,招待交换! 下后生可畏篇:《深入显出WPF》笔记——命令篇

     

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

    换来平常语言正是,事件能够定义成静态的或普通的,所以事件就足以由评释的对象调用,也得以直接通过类调用静态事件。

    事件是C#中的生机勃勃体系型,除了框架为大家定义好的事件外,我们还足以自定义事件,用event关键字来声称。

    上边大家来看最幼功的风浪定义。

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

    我们第一定义了二个信托,然后选用event关键字,定义四个平地风波。

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

    不错,事件的概念正是那样,因为要声圣元(Synutra卡塔尔个事变,要求多个因素:

    大器晚成,标记提供对事件的响应的艺术的委托。

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

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

    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等。

    个中最优越的便是伊芙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多个参数。

    但Routed伊夫ntHandler特别之处是,他的sender并不一定是真实的源,因为她是四个冒泡路由事件,即上涨事件。

    此处若是大家有好奇心去看官方文书档案,那么会在有关的牵线中看到五个单词sender和source。

    经过那五个单词,我们会清楚的垂询路由事件。轻便描述一下sender和source,它们二个是发送者,四个是源。

    在伊芙ntHandler中,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

     

    本文由澳门新葡8455最新网站发布于编程教学,转载请注明出处:逐渐边缘化的大哥,深入浅出WPF

    关键词: