澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

澳门新萄京官方网站正规事件形式

2019-10-12 作者:www.8455.com   |   浏览(155)

.NET框架为事件定义了一个标准模式,它的目的是保持框架和用户代码之间的一致性。

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

 事件,我相信开始学C#的朋友都会用过,在C#中很常见,比如点击一个按钮,上传一张图片等等,在WinForm或WebForm中都在使用着事件。今天,趁着有少少事件,我决定来重温一下之前被自己略过的东西

标准事件的模式核心是SystemEventArgs——预定义的没有成员的框架类(不同于静态Empty属性)

     在写代码前我们先来熟悉.net框架中和事件有关的类和委托,了解C#中预定义事件的处理。

  • 事件。

EventArgs表示包含事件数据的类的基类,并提供用于不包含事件数据的事件的值。用于为事件传递信息的基类。

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

  好记得在之前,在用一个方法的时候,如果参数里面有个Handler,就好害怕,其实事件还是用委托来做中介的,在事件上两次转到定义就去到委托了,将委托复制出来,去掉delegate就是方法签名了,写上自己要实现的代码给事件赋值就OK了。

在下面例子中,我们定义EventArgs的子类,用于事件PriceChanged被引发时,传递新旧Price值:

     EventHandler是一个委托声明如下

  一、什么是事件

    public class PriceChangedEventArgs : EventArgs
    {
        public readonly decimal LastPrice;
        public readonly decimal NewPrice;

        public PriceChangedEventArgs(decimal lastPrice, decimal newPrice)
        {
            LastPrice=lastPrice;
            NewPrice= newPrice;
        }
    }

          public delegate void EventHandler( object sender , EventArgs e )

  事件涉及两类角色:事件的发布者和事件的订阅者。触发事件的对象称为事件发布者,捕获时间并对其作出响应的对象叫做事件订阅者。

考虑到复用性,EventArgs子类根据它包含的内容命名(而非根据将被使用的事件命名)。

     注意这里的参数,前者是一个对象(其实这里传递的是对象的引用,如果是button1的click事件则sender就是button1),后面是包含事件数据的类的基类。

  二、事件和委托的关系

选择或定义事件的委托,需遵循三条原则:

     下面我们研究一下Button类看看其中的事件声明(使用WinCV工具查看),以Click事件为例。

  在事件触发以后,事件发布者需要发布消息,通知事件订阅者进行事件处理,但事件发布者并不知道要通知哪些事件订阅者,这就需要在发布者和订阅者之间存在一个中介,这个中介就是委托。我们知道,委托都有一个调用列表,那么,只需要事件发布者有这样一个委托,各个事件订阅者将自己的事件处理程序都加入到该委托的调用列表中,那么事件触发时,发布者只需要调用委托即可触发订阅者的事件处理程序。

  • 委托必须以void作为返回值
  • 委托必须接受两个参数:第一个是object类,第二个是EventArgs的子类。
  • 委托的名称必须以EventHandler结尾

          public event EventHandler Click;

  三、如何声明事件

 

     这里定义了一个EventHandler类型的事件Click

  声明事件的语法和定义一个类的成员非常相似,也非常简单。事实上,事件就是类成员的一种,只是事件定义中包含一种特殊的关键字:event。

完整例子:

     前面的内容都是C#在类库中已经为我们定义好了的。下面我们来看编程时产生的代码。

  事件的声明有两种方式:

class Test 
        public static void Main()
        {
            InitializeComponent();
            Stock stock = new Stock("THPW");
            stock.Price = 27.10M;
            //注册PriceChanged事件
            stock.PriceChanged  = stock_PriceChanged;
            stock.Price = 31.59M;
        }

        static void stock_PriceChanged(object sender, PriceChangedEventArgs e)
        {
            if ((e.NewPrice - e.LastPrice) / e.LastPrice > 0.1M)
            {
                Console.WriteLine("Alert,10% stock increase!");
            }
        }
    }

    public class Stock
    {
        string symbol;
        decimal price;

        public Stock(string symbol)
        {
            this.symbol = symbol;
        }

        //定义委托事件
        public event EventHandler<PriceChangedEventArgs> PriceChanged;


        protected virtual void OnPriceChanged(PriceChangedEventArgs e)
        {
            if (PriceChanged != null) PriceChanged(this, e);
        }

        public decimal Price
        {
            get { return price; }
            set
            {
                if (price == value) return;

            price = value;

          OnPriceChanged(new PriceChangedEventArgs(price, value));

            }
        }
    }

    public class PriceChangedEventArgs : EventArgs
    {
        public readonly decimal LastPrice;
        public readonly decimal NewPrice;

        public PriceChangedEventArgs(decimal lastPrice, decimal newPrice)
        {
            LastPrice=lastPrice;
            NewPrice= newPrice;
        }
    }    

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

  1、采用自定义委托类型。

 

     这是我们和button1_click事件所对应的方法。注意方法的参数符合委托中的签名(既参数列表)。那我们怎么把这个方法和事件联系起来呢,请看下面的代码。

  2、采用EventHandler预定义委托类型。

如果事件不传递额外的信息,可以使用预定义的非泛化委托EventHandler。如下所示:

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

  这两种方式基本相同,只不过第二种是.Net Framework中普遍采用的一种形式,因此建议尽量采用第二种方式。

    class Test
    {
        public static void Main()
        {
            InitializeComponent();
            Stock stock = new Stock();
            stock.Price = 27.10M;
            //注册PriceChanged事件
            stock.PriceChanged  = stock_PriceChanged;
            stock.Price = 31.59M;
        }

        static void stock_PriceChanged(object sender, EventArgs e)
        {
           Console.WriteLine("价格变换了!");
        }
    }

    public class Stock
    {
        decimal price;

        public event EventHandler PriceChanged;

        protected virtual void OnPriceChanged(EventArgs e)
        {
            if (PriceChanged != null) PriceChanged(this, e);
        }

        public decimal Price
        {
            get { return price; }
            set
            {
                if (price == value) return;
                price = value;

                //OnPriceChanged(new EventArgs());

                OnPriceChanged(EventArgs.Empty);
            }
        }
    }

     把this.button1_Click方法绑定到this.button1.Click事件。

    //     关键字  委托类型     时间名
    public event  EventHandler PrintComplete;

 

     下面我们研究一下C#事件处理的工作流程,首先系统会在为我们创建一个在后台监听事件的对象(如果是 button1的事件那么监听事件的就是button1),这个对象用来产生事件,如果有某个用户事件发生则产生对应的应用程序事件,然后执行订阅了事件 的所有方法。

  EventHandler是在BCL中预定义的委托类型,它位于System命名空间,用以处理不包含事件数据的事件。事件如果需要包含事件数据,可以通过派生EventArgs实现。

注意:

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

  先来看看EventHandler委托的签名:

上面例子中事件除了传递已发生信息,没有传递其他信息。

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

public delegate void EventHandler(Object sender,EventArgs e);

你可以使用 OnPriceChanged(new EventArgs()) 来完成事件的传递。

     定义一个委托。

  1、委托的返回类型为void;

为了避免对EventArgs不必要的初始化,建议使用EventArgs.Empty属性。使用这样一个“空的”静态引用的对象,避免多余地去创建一个新对象。

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

  2、第一个参数--sender参数,它负责保存触发事件的对象的引用,因为参数的类型是Object类型,因此它可以保存任何类型的实例;

 

     前面的object用来传递事件的发生者,后面的EventArgs用来传递事件的细节,现在暂时没什么用处,一会后面的例子中将使用。

  3、第二个参数--e参数,它负责保存事件数据,这里是在BCL中定义的默认的EventArgs类,它位于System命名空间中,他不能保存任何数据。

微软的大部分控件所抛出的事件都有两个参数,第一个是 object 类型的,第二个是 EventArgs 类型的。

     下面定义一个此委托类型类型的事件

  四、订阅事件

澳门新萄京官方网站 1

         public event UserRequest OnUserRequest;

  事件订阅者角色需要订阅事件发布者发布的事件,这样才能在事件发布时接受到消息并作出响应,事件事实上是委托类型,因此事件处理方法必须和委托签名相匹配。如果事件使用预定义的委托类型:EventHandler,那么匹配它的事件处理方法如下:

你从EventArgs  e那里得不到任何此次事件相关需要传递的信息,真正传递的信息都在sender中。

     下面我们来做一个死循环

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

  有了事件处理方法,就可以订阅事件了,只需要使用加法赋值运算符( =)即可。

     此代码不断的要求用户输入字符,如果输入的结果是h,则触发OnUserRequest事件,事件的触发者是本身(this),事件细节无(没有传递任何参数的EventArgs实例)。我们给这个类取名为UserInputMonitor。

  五、触发事件

    下面我们要做的是定义客户端的类
     首先得实例化UserInputMonitor类
    //检查事件是否为空
    if (PrintComplete != null)
    { 
        //像调用方法一样触发事件,参数
        PrintComplete(this,new EventArgs();
    }

        UserInputMonitor monitor=new UserInputMonitor();

澳门新萄京官方网站正规事件形式。  完整的一个事件例子:

     然后我们定义一个方法。

澳门新萄京官方网站 2

       private void ShowMessage(object sender,EventArgs e)
澳门新萄京官方网站正规事件形式。       {
           Console.WriteLine("HaHa!!");
       }

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("该做的东西做完,然后触发事件!");
            EventSample es = new EventSample();
            es.ShowComplete  = es.MyEventHandler;
            es.OnShowComplete();

            Console.ReadKey();
        }
    }

    public class EventSample
    {
     //定义一个事件
        public event EventHandler ShowComplete;
    
     //触发事件
        public void OnShowComplete()
        {
            //判断是否绑定了事件处理方法,null表示没有事件处理方法
            if (ShowComplete != null)
            {
          //像调用方法一样触发事件
                ShowComplete(this, new EventArgs());
            }
        }

        //事件处理方法
        public void MyEventHandler(object sender, EventArgs e)
        {
            Console.WriteLine("谁触发了我?"   sender.ToString());
        }
    }
}

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

澳门新萄京官方网站 3

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

  六、使用和扩展EventArgs类

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

  前面提到过,默认的预定义委托EventHandler的第二个参数,本身不能包含事件数据。但是在很多.net提供的方法,都能用e调用出某些信息这是因为这不是默认的EventArgs类了。因此,在事件引发时不能向事件处理程序传递状态信息,如果要想传递状态信息,则需要从此类派生出一个类来保存信息。

      }

  以下为扩展EventArgs类的示例:

      下面创建客户端的实例。

澳门新萄京官方网站 4

        new Client(monitor);

    public class PrintEventArgs : EventArgs
    {
        public string PrintState
        {
            get;
            set;
        }

        public PrintEventArgs(string state)
        {
            PrintState = state;
        }
    }

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

澳门新萄京官方网站 5

         monitor.run();

  而在调用时,只需将EventArgs换成PrintEventArgs

      大功告成,代码如下:

    public void SomeEventHandler(object sender, PrintEventArgs e)
    {
        Console.WriteLine("打印已完成!");
    }
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);
  //注意这种写法是错误的,因为委托是静态的
 }
}

  但是要注意此时,绑定事件编译器会报错:

三、进一步研究C#中的预定义事件处理机制

  enentSample.PrintComplete  = ShowMessage;  //此行代码编译器报错

     可能大家发现在C#中有些事件和前面的似乎不太一样。例如

  为什么呢?因此事件委托的第二个参数是EventArgs类型与扩展的PrintEventArgs不同,因此不能在绑定旧的方法;所以就要使用自定义委托了。

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

  七、自定义委托

       }

  既然EventHandler委托不能使用了,那么就只有考虑使用自定义委托来声明事件了。首先声明一个自定义委托:

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

  public delegate void PrintEventDelegate(object sender,PrintEventArgs e);

     这里使用了KeyPressEventArgs而不是EventArgs作为参数。这里使用了KeyEventHandler委托,而不是EventHandler委托。

  接下来,将事件声明中使用的预定义委托EventDelegate换成我们自定义的委托:

    KeyPressEventArgs是EventArgs的派生类,而KeyEventHandler的声明如下

  public event PrintEventDelegate PrintComplete;

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

  因为我们扩展的PrintEventArgs类没有不带参数的构造函数,因此需要修改事件触发部分的代码,传递一个参数进去,该参数的值就是要发送给事件处理方法的状态信息,这里以一个简单的字符串代替:

    是参数为KeyEventArgs的委托。那为什么KeyPress事件要这么做呢,我们可以从两个类的构造函数来找答案。

  if(PrintComplete != null)
  {
    PrintComplete(this,new PrintEventArgs("测试消息"));
  }

       public EventArgs();

  八、事件访问器

       public KeyPressEventArgs(char keyChar);

  事件是特殊的多路广播委托,事件默认有一个私有的委托类型变量,用以保存对订阅事件的事件处理方法的引用,此委托类型的变量仅能从声明该事件的类中委托。事件订阅者通过提供对事件处理方法的引用来订阅事件,这些方法通过默认的时间访问器添加到委托的调用列表中。这里的事件访问器类似于属性访问器,不同之处在于,时间访问器被命名为add和remove,而不是属性的get和set。在大多数情况下都不需要提供自定义的事件访问器。如果没有提供,则编译器会自动添加事件访问器。如果需要添加自定义事件访问器,以支持某些自定义行为,可以使用如下语法:

     这里的keyData是什么,是用来传递我们按下了哪个键的,哈。

澳门新萄京官方网站 6

     我在KeyEventArgs中又发现了属性

  public event MyEventHandler PrintComplete
  {
    add
    {
      //..
    }
    remove
    {
      //..
    }
  }

        public char KeyChar { get; }

澳门新萄京官方网站 7

     进一步证明了我的理论。下面我们来做一个类似的例子来帮助理解。

  在声明了事件访问器以后,编译器将不会提供私有的委托对象,此时对于订阅者事件处理方法引用的管理需要我们自己去实现。

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

    public event MyEventHandler PrintComplete
    {
        add { myEventHandler  = value; }
        remove { myEventHandler -= value; }
    }

    拿我们上面做的例子来改。

   下面给出一个扩展EventArgs与自定义委托,传递数据到方法的示例:

     我们也定义一个EventArgs(类似KeyEventArgs)取名MyEventArgs,定义一个构造函数public MyEventArgs(char keyChar),同样我们也设置相应的属性。代码如下

澳门新萄京官方网站 8

using System;
class MyMyEventArgs:EventArgs
{
 private char keyChar;
 public MyMyEventArgs(char keyChar)
 {
  this.keychar=keychar;
 }
 public char KeyChar
 {
  get
  {
   return keyChar;
  }
 }
}
namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("该做的东西做完,然后触发事件!");
            EventSample es = new EventSample();
            es.ShowComplete  = es.MyEventHandler;
            es.OnShowComplete();

            Console.ReadKey();
        }
    }

    public class EventSample
    {
        //此事件已不能如此使用
        //public event EventHandler ShowComplete;

        //自定义委托
        public delegate void ShowEventDelegate(object sender,ShowEventArgs e);

        //将事件中的委托换成自己的自定义委托
        public event ShowEventDelegate ShowComplete; 

        public void OnShowComplete()
        {
            //判断是否绑定了事件处理方法,null表示没有事件处理方法
            if (ShowComplete != null)
            {
                //这次要传递参数数据了
                ShowComplete(this, new ShowEventArgs("传给你的数据,接着吧!"));
            }
        }

        //事件处理方法,注意第二个参数
        public void MyEventHandler(object sender, ShowEventArgs e)
        {
            Console.WriteLine("谁触发了我   "   sender.ToString());
            Console.WriteLine("传过来什么数据:   "   e.ShowResult);
        }
    }

    //自定义EventArgs
    public class ShowEventArgs : EventArgs
    {
        public string ShowResult
        {
            get;
            set;
        }

        public ShowEventArgs(string result)
        {
            ShowResult = result;
        }
    }
}

因为现在要监听多个键了,我们得改写监听器的类中的do...while部分。改写委托,改写客户端传递的参数。好了最终代码如下,好累

澳门新萄京官方网站 9

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);
  //注意这种写法是错误的,因为委托是静态的
 }
}

  输出结果如下:

  澳门新萄京官方网站 10

  九、事件的综合说明

  在事件中,一共有4个主要东西。

1、sender:传递触发委托的对象;
2、EventArgs:传递事件的细节;
3、EventHandler:用于接受委托方法;
4、delegate:方法的包装,允许将方法传递过去;

  如当点击一个按钮时,执行委托(EventHandler所绑定的方法),并告诉委托程序,是按钮1(object sender)被点击(EventArgs e)了。  

  很多时候,由于.Net自带的这些基类不能够满足我们需要传递的多个参数,所以有时候我们需要自定义各种继承类。

  例如,我们在Winform上拖入一个文本框,并设置textBox1_MouseClick事件,则生成的代码如下:

private void textBox1_MouseClick(object sender, MouseEventArgs e)
{
  Console.WriteLine(e.Clicks);
}

  我们看到原本的EventArgs变成了MouseEventArgs。

  其代码如下,对比之下,它能够传递更多的参数了。

澳门新萄京官方网站 11

    // 摘要: 
    //     为 System.Windows.Forms.Control.MouseUp、System.Windows.Forms.Control.MouseDown
    //     和 System.Windows.Forms.Control.MouseMove 事件提供数据。
    [ComVisible(true)]
    public class MouseEventArgs : EventArgs
    {
        // 摘要: 
        //     初始化 System.Windows.Forms.MouseEventArgs 类的新实例。
        //
        // 参数: 
        //   button:
        //     System.Windows.Forms.MouseButtons 值之一,指示按下的鼠标按钮。
        //
        //   clicks:
        //     鼠标按钮曾被按下的次数。
        //
        //   x:
        //     鼠标单击的 x 坐标(以像素为单位)。
        //
        //   y:
        //     鼠标单击的 y 坐标(以像素为单位)。
        //
        //   delta:
        //     鼠标轮已转动的制动器数的有符号计数。
        public MouseEventArgs(MouseButtons button, int clicks, int x, int y, int delta);
        //     获取曾按下的是哪个鼠标按钮。
        public MouseButtons Button { get; }
        //     获取按下并释放鼠标按钮的次数。
        public int Clicks { get; }
        //     获取鼠标轮已转动的制动器数的有符号计数乘以 WHEEL_DELTA 常数。 制动器是鼠标轮的一个凹口。
        public int Delta { get; }
        //     获取鼠标在产生鼠标事件时的位置。
        public Point Location { get; }
        //     获取鼠标在产生鼠标事件时的 x 坐标。
        public int X { get; }
        //     获取鼠标在产生鼠标事件时的 y 坐标。
        public int Y { get; }
    }

澳门新萄京官方网站 12

 

 

  归根到底,object sender与EventArgs e都是为了传递参数。自定义的可以传递更多参数。

 

 

  事件实际运用的Demo:

 

转自:

本文由澳门新萄京官方网站发布于www.8455.com,转载请注明出处:澳门新萄京官方网站正规事件形式

关键词: