zoukankan      html  css  js  c++  java
  • C#委托与事件学习笔记

        本笔记摘抄自:https://www.cnblogs.com/leslies2/archive/2012/03/22/2389318.html,记录一下学习过程以备后续查用。

        一、委托类型的来由

        在使用C语言的年代,整个项目中都充满着针指的身影,那时候流行使用函数指针来创建回调函数,使用回调可以把函数回调给程序中的另一个函数。但函数指针

    只是简单地把地址指向另一个函数,并不能传递其他额外信息。

        在.NET中,大部分时间里都没有指针的身影,因为指针被封闭在内部函数当中。可是回调函数却依然存在,它是以委托的方式来完成的。委托可以被视为一个更

    高级的指针,它不仅仅能把地址指向另一个函数,而且还能传递参数、返回值等多个信息。系统还为委托对象自动生成了同步、异步的调用方式,开发人员使用

    BeginInvoke、EndInvoke方法就可以抛开Thread而直接使用多线程调用 。

        二、建立委托类

        使用delegate可以直接创建委托类型,当进行系统编译时,系统就会自动生成此类型,可以使用delegate void MyDelegate()方式创建一个委托类。

        class Program
        {
            delegate void MyDelegate();
            static void Main(string[] args)
            {
                Console.WriteLine("Hello World.");
                Console.Read();
            }
        }
    View Code

        使用ILDASM.exe观察委托成员,可以看到它继承了System.MulticastDelegate类,并自动生成BeginInvoke、EndInvoke、Invoke 等三个常用方法。

        Invoke 方法是用于同步调用委托对象的对应方法,而BeginInvoke、EndInvoke是用于以异步方式调用对应方法。

        public class MyDelegate:MulticastDelegate
         {
             //同步调用委托方法
             public virtual void Invoke();
             //异步调用委托方法
             public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state);
             public virtual void EndInvoke(IAsyncResult result);
         }
    View Code

        MulticastDelegate是System.Delegate的子类,它是一个特殊类,编译器和其他工具可以从此类派生,但是自定义类不能显式地从此类进行派生。它支持多路

    广播委托,并拥有一个带有链接的委托列表。在调用多路广播委托时,系统将按照调用列表中的委托出现顺序来同步调用这些委托。

        MulticastDelegate具有两个常用属性:Method、Target。其中Method用于获取委托所表示的方法,Target用于获取当前调用的类实例。

        MulticastDelegate有以下几个常用方法:

    方法名称说明
     Clone   创建委托的浅表副本。
     GetInvocationList   按照调用顺序返回此多路广播委托的调用列表。
     GetMethodImpl   返回由当前的 MulticastDelegate 表示的静态方法。
     GetObjectData   用序列化该实例所需的所有数据填充 SerializationInfo 对象。
     MemberwiseClone   创建当前 Object 的浅表副本。
     RemoveImpl   调用列表中移除与指定委托相等的元素

        MulticastDelegate与Delegate给委托对象建立了强大的支持。

        三、委托使用方式    

        3.1 简单的委托

        当建立委托对象时,委托的参数类型必须与委托方法相对应。只要向建立委托对象的构造函数中输入方法名称example.Method,委托就会直接绑定此方法。

    使用myDelegate.Invoke(string message),就能显式调用委托方法。

        但在实际的操作中,我们无须用到Invoke方法,而只要直接使用myDelegate(string message),就能调用委托方法。

        下面代码演示简单的委托:

        class Program
        {
            delegate void MyDelegateVoid(string message);
    
            public class Example
            {
                public void ShowMessage(string message)
                {
                    Console.WriteLine(message);
                }
            }
    
            static void Main(string[] args)
            {
                #region 简单的委托
                Example example = new Example();
                MyDelegateVoid myDelegateVoid = new MyDelegateVoid(example.ShowMessage);
                myDelegateVoid("Hello World");
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        3.2 带返回值的委托

        当建立委托对象时,委托的返回值必须与委托方法相对应。

        下面代码演示带返回值的委托:

        class Program
        {
            delegate string MyDelegateString(string message);
    
            public class Example
            {
                public string SayHi(string name)
                {
                    return "Hello " + name;
                }
            }
    
            static void Main(string[] args)
            {
                #region 带返回值的委托
                Example example = new Example();
                MyDelegateString myDelegateString = new MyDelegateString(example.SayHi);
                string message = myDelegateString("Atomy");
                Console.WriteLine(message);
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        3.3 多路广播委托

        在第二节前曾经提过,委托类继承于MulticastDelegate,这使委托对象支持多路广播,即委托对象可以绑定多个方法。

        下面代码演示多路广播委托:

        class Program
        {
            delegate double MyDelegateDouble(double message);
    
            public class Example
            {
                public double Ordinary(double price)
                {
                    double price1 = 0.95 * price;
                    Console.WriteLine($"Ordinary price={price1}");
                    return price1;
                }
    
                public double Favourable(double price)
                {
                    double price1 = 0.85 * price;
                    Console.WriteLine($"Favourable price={price1}");
                    return price1;
                }
            }
    
            static void Main(string[] args)
            {
                #region 多路广播委托
                Example example = new Example();
                MyDelegateDouble myDelegateDouble = new MyDelegateDouble(example.Ordinary);
                myDelegateDouble += new MyDelegateDouble(example.Favourable);
                Console.WriteLine($"Current Price={myDelegateDouble(100)}");
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        3.4 浅谈Observer模式(观察者模式)

        简单回顾一下Observer模式,它使用一对多的方式,可以让多个观察者同时关注同一个事物,并作出不同的响应。

        如下例,Manager的底薪为基本工资的1.5倍,Assistant的底薪为基本工资的1.2倍。WageManager类的RegisterWorker方法与RemoveWorker方法可以

    用于注册和注销观察者,最后执行Execute方法可以对多个已注册的观察者同时输入参数。

        下面代码演示使用非委托方式实现观察者模式:

        class Program
        {
            #region 非委托观察者模式
            /// <summary>
            /// 工作者类
            /// </summary>
            public abstract class Worker
            {
                public abstract double GetWages(double basicWages);
            }
    
            /// <summary>
            /// 管理级类
            /// </summary>
            public class Manager : Worker
            {
                public override double GetWages(double basicWages)
                {
                    double totalWages = 1.5 * basicWages;
                    Console.WriteLine($"Manager's wages is:{totalWages}");
                    return totalWages;
                }
            }
    
            /// <summary>
            /// 助理级类
            /// </summary>
            public class Assistant : Worker
            {
                public override double GetWages(double basicWages)
                {
                    double totalWages = 1.2 * basicWages;
                    Console.WriteLine($"Assistant's wages is:{totalWages}");
                    return totalWages;
                }
            }
    
            /// <summary>
            /// 工资管理类
            /// </summary>
            public class WageManager
            {
                IList<Worker> workerList = new List<Worker>();
    
                public void RegisterWorker(Worker worker)
                {
                    workerList.Add(worker);
                }
    
                public void RemoveWorker(Worker worker)
                {
                    workerList.Remove(worker);
                }
    
                public void Excute(double basicWages)
                {
                    if (workerList.Count != 0)
                    {
                        foreach (var worker in workerList)
                        {
                            worker.GetWages(basicWages);
                        }
                    }
                }
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 非委托观察者模式
                WageManager wageManager = new WageManager();
                //注册观察者
                wageManager.RegisterWorker(new Manager());
                wageManager.RegisterWorker(new Assistant());
                //同时输入底薪3000元,分别进行计算。
                wageManager.Excute(3000);
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        开发Observer模式时若借助委托,可以进一步简化开发过程。由于委托对象支持多路广播,所以可以把Worker类省略。在WageManager类中建立了一个

    委托对象wageHandler,通过Attach与Detach方法可以分别加入及取消委托。如果观察者想对事物进行监测,只需要加入一个委托对象即可。在第二节提过,

    委托的GetInvodationList方法能获取多路广播委托列表,在Execute方法中,就是通过多路广播委托列表去判断所绑定的委托数量是否为0。

        下面代码演示使用委托方式实现观察者模式:

        class Program
        {
            #region 委托观察者模式
            public delegate double Handler(double basicWages);
    
            public class Manager
            {
                public double GetWages(double basicWages)
                {
                    double totalWages = 1.5 * basicWages;
                    Console.WriteLine($"Manager's wages is:{totalWages}");
                    return totalWages;
                }
            }
    
            public class Assistant
            {
                public double GetWages(double basicWages)
                {
                    double totalWages = 1.2 * basicWages;
                    Console.WriteLine($"Assistant's wages is:{totalWages}");
                    return totalWages;
                }
            }
    
            public class WageManager
            {
                private Handler wageHandler;
    
                //加入观察者
                public void Attach(Handler wageHandler1)
                {
                    wageHandler += wageHandler1;
                }
    
                //删除观察者
                public void Detach(Handler wageHandler1)
                {
                    wageHandler -= wageHandler1;
                }
    
                //通过GetInvodationList方法获取多路广播委托列表,如果观察者数量大于0即执行方法。
                public void Execute(double basicWages)
                {
                    if (wageHandler != null)
                    {
                        if (wageHandler.GetInvocationList().Count() != 0)
                        {
                            wageHandler(basicWages);
                        }
                    }
                }
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 委托观察者模式
                WageManager wageManager = new WageManager();
                //加入Manager观察者
                Manager manager = new Manager();
                Handler managerHandler = new Handler(manager.GetWages);
                wageManager.Attach(managerHandler);
    
                //加入Assistant观察者
                Assistant assistant = new Assistant();
                Handler assistantHandler = new Handler(assistant.GetWages);
                wageManager.Attach(assistantHandler);
    
                //同时加入底薪3000元,分别进行计算
                wageManager.Execute(3000);
                Console.ReadKey();
                #endregion
            }
        }
    View Code

        运行结果如下:

        3.5 委托的协变与逆变

        在Framework 2.0出现之前,委托协变这个概念还没有出现。此时因为委托是安全类型,它们不遵守继承的基础规则。即会这下面的情况:Manager虽然

    是Worker的子类,但GetWorkerHander委托不能直接绑定GetManager方法,因为在委托当中它们的返回值Manager与Worker被视为完全无关的两个类型。

        自Framework 2.0面世以后,委托协变的概念就应运而生,此时委托可以按照传统的继承规则进行转换。即GetWorkerHandler委托可以直接绑定

    GetManager方法。

        下面代码演示委托的协变:

        class Program
        {
            #region 委托的协变
            /// <summary>
            /// 在Framework 2.0以上可绑定GetWorker与GetManager两个方法
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            public delegate Worker GetWorkerHandler(int id);
    
            public class Worker
            {
                public Worker() { }
    
                public Worker(int id)
                {
                    Id = id;
                }
    
                public int Id { get; set; }
    
                public void ShowId()
                {
                    Console.WriteLine($"Id={Id}");
                }
            }
    
            public class Manager : Worker
            {
                public Manager() { }
                public Manager(int id)
                {
                    Id = id;
                }
            }
    
            public static Worker GetWorker(int id)
            {
                Worker worker = new Worker(id);
                return worker;
            }
    
            public static Manager GetManager(int id)
            {
                Manager manager = new Manager(id);
                return manager;
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 委托的协变
                GetWorkerHandler workerHandler = new GetWorkerHandler(GetWorker);
                Worker worker = workerHandler(1);
                worker.ShowId();
                GetWorkerHandler managerHandler = new GetWorkerHandler(GetManager);
                Manager manager = managerHandler(2) as Manager;
                manager.ShowId();
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        委托逆变,是指委托方法的参数同样可以接收 “继承” 这个传统规则。像下面的例子,以object为参数的委托,可以接受任何object子类的对象作为参数。

    最后可以在处理方法中使用is对输入数据的类型进行判断,分别处理对不同的类型的对象。

        下面代码演示委托的逆变:

        class Program
        {
            #region 委托的逆变
            public delegate void Handler(object obj);
    
            public static void GetMessage(object message)
            {
                if (message is string)
                    Console.WriteLine("His name is:" + message.ToString());
                if (message is int)
                    Console.WriteLine("His age is:" + message.ToString());
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 委托的逆变
                Handler handler = new Handler(GetMessage);
                handler(29);
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        注:委托与其绑定方法的参数必须一至,即当 Handler 所输入的参数为 A 类型,其绑定方法 GetMessage 的参数也必须为 A 类或者 A 的父类 。相反,

    当绑定方法的参数为 A 的子类,系统也无法辨认。

        3.6 泛型委托

        委托逆变虽然实用,但如果都以object作为参数,则需要每次都对参数进行类型的判断,这不禁令人感到厌烦。

        为此,泛型委托应运而生,泛型委托有着委托逆变的优点,同时利用泛型的特性,可以使一个委托绑定多个不同类型参数的方法,而且在方法中不需要

    使用is进行类型判断,从而简化了代码。

        下面代码演示泛型委托:

        class Program
        {
            #region 泛型委托
            public delegate void Handler<T>(T obj);
    
            /// <summary>
            /// 工作者类
            /// </summary>
            public class Worker
            {
                public double Wages { get; set; }
            }
    
            /// <summary>
            /// 管理级类
            /// </summary>
            public class Manager : Worker
            {
            }
    
            public static void GetWorkerWages(Worker worker)
            {
                Console.WriteLine("Worker's total wages is:" + worker.Wages);
            }
    
            public static void GetManagerWages(Manager manager)
            {
                Console.WriteLine("Manager's total wages is:" + manager.Wages);
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 泛型委托
                Handler<Worker> workerHander = new Handler<Worker>(GetWorkerWages);
                Worker worker = new Worker
                {
                    Wages = 3000
                };
                workerHander(worker);
    
                Handler<Manager> managerHandler = new Handler<Manager>(GetManagerWages);
                Manager manager = new Manager
                {
                    Wages = 4500
                };
                managerHandler(manager);
    
                Console.ReadKey();
                #endregion
            }
        }
    View Code

        运行结果如下:

        四、深入解析事件

        4.1 事件的由来

        在介绍事件之前大家可以先看看下面的例子,PriceManager负责对商品价格进行处理,当委托对象GetPriceHandler的返回值大于100元,按8.8折计算,

    低于100元按原价计算。

        class Program
        {
            #region 事件的由来
            public delegate double PriceHandler();
            public class PriceManager
            {
                public PriceHandler GetPriceHandler;
    
                //委托处理,当价格高于100元按8.8折计算,其他按原价计算。
                public double GetPrice()
                {
                    if (GetPriceHandler.GetInvocationList().Count() > 0)
                    {
                        if (GetPriceHandler() > 100)
                            return GetPriceHandler() * 0.88;
                        else
                            return GetPriceHandler();
                    }
                    return -1;
                }
            }
            //书本价格为98元
            public static double BookPrice()
            {
                return 98.0;
            }
            //计算机价格为8800元
            public static double ComputerPrice()
            {
                return 8800.0;
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 事件的由来
                PriceManager priceManager = new PriceManager
                {
                    //调用priceManager的GetPrice方法获取价格
                    //直接调用委托的Invoke获取价格,两者进行比较。
                    GetPriceHandler = new PriceHandler(ComputerPrice)
                };
                Console.WriteLine(string.Format("GetPrice
     Computer's price is {0}",priceManager.GetPrice()));
                Console.WriteLine(string.Format("Invoke
     Computer's price is {0}",priceManager.GetPriceHandler.Invoke()));
                Console.WriteLine();
                priceManager.GetPriceHandler = new PriceHandler(BookPrice);
                Console.WriteLine(string.Format("GetPrice
     Book's price is {0}",priceManager.GetPrice()));
                Console.WriteLine(string.Format("Invoke
     Book's price is {0}",priceManager.GetPriceHandler.Invoke()));
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        观察运行的结果,如果把委托对象GetPriceHandler设置为public,外界可以直接调用GetPriceHandler.Invoke获取运行结果而移除了GetPrice方法的处理,

    这正是开发人员最不想看到的。

        为了保证系统的封装性,开发往往需要把委托对象GetPriceHandler设置为private,再分别加入AddHandler、RemoveHandler方法对GetPriceHandler委托

    对象进行封装。为了保存封装性,很多操作都需要加入AddHandler、RemoveHandler这些相似的方法代码,这未免令人感到厌烦。

        为了进一步简化操作,事件这个概念应运而生。

        4.2 事件的定义

        事件(event)可被视作为一种特别的委托,它为委托对象隐式地建立起add_XXX、remove_XXX两个方法,用作注册与注销事件的处理方法,而且事件对

    应的变量成员将会被视为private变量,外界无法超越事件所在对象直接访问它们,这使事件具备良好的封装性,而且免除了add_XXX、remove_XXX等繁琐

    的代码。

    #region 事件的定义
    public class EventTest
    {
        public delegate void MyDelegate();
        public event MyDelegate MyEvent;
    }
    #endregion

        使用ILDASM.exe观察事件成员,系统为MyEvent事件自动建立add_MyEvent、remove_MyEvent 方法。

        4.3 事件的使用方式

        事件能通过+=和-=两个方式注册及注销对其处理的方法,使用+=与-=操作符的时候,系统会自动调用对应的add_XXX、remove_XXX进行处理。

        值得留意,在PersonManager类的Execute方法中,如果MyEvent绑定的处理方法不为空,即可使用MyEvent(string)引发事件。但如果在外界的Main方法中

    直接使用personManager.MyEvent(string)来引发事件,系统将引发错误报告。这正是因为事件具备了良好的封装性,使外界不能超越事件所在的对象访问其变

    量成员。

        注:在事件所处的对象之外,事件只能出现在+=、-=的左方。

        下面代码演示事件的使用:

        class Program
        {
            #region 事件的使用
            public delegate void MyDelegate(string name);
    
            public class PersonManager
            {
                public event MyDelegate MyEvent;
    
                //执行事件
                public void Execute(string name)
                {
                    if (MyEvent != null)
                    {
                        MyEvent(name);
                    }
                }
            }
    
            public static void GetName(string name)
            {
                Console.WriteLine("My name is " + name);
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 事件的使用
                PersonManager personManager = new PersonManager();
                //绑定事件处理方法
                personManager.MyEvent += new MyDelegate(GetName);
                personManager.Execute("Atomy");
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        4.4 事件处理方法的绑定

        在绑定事件处理方法的时候,事件出现在+=、-= 操作符的左边,对应的委托对象出现在+=、-= 操作符的右边。对应以上例子,事件提供了更简单的绑定方式,

    只需要在+=、-= 操作符的右方写上方法名称,系统就能自动辩认。

        下面代码演示事件处理方法的绑定:

        class Program
        {
            #region 事件的使用及方法绑定
            public delegate void MyDelegate(string name);
    
            public class PersonManager
            {
                public event MyDelegate MyEvent;
    
                //执行事件
                public void Execute(string name)
                {
                    if (MyEvent != null)
                    {
                        MyEvent(name);
                    }
                }
            }
    
            public static void GetName(string name)
            {
                Console.WriteLine("My name is " + name);
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 事件的使用及方法绑定
                PersonManager personManager = new PersonManager();
                //绑定事件处理方法方式一
                personManager.MyEvent += new MyDelegate(GetName);
                //绑定事件处理方法方式二
                personManager.MyEvent += GetName;
                personManager.Execute("Atomy");
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        如果觉得编写GetName方法过于麻烦,还可以使用匿名方法绑定事件的处理。

        下面代码演示事件处理方法的匿名方法绑定:

        class Program
        {
            #region 事件的使用
            public delegate void MyDelegate(string name);
    
            public class PersonManager
            {
                public event MyDelegate MyEvent;
    
                //执行事件
                public void Execute(string name)
                {
                    if (MyEvent != null)
                    {
                        MyEvent(name);
                    }
                }
            }
    
            public static void GetName(string name)
            {
                Console.WriteLine("My name is " + name);
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 事件的使用及方法绑定
                PersonManager personManager = new PersonManager();
                //绑定事件处理方法方式一
                personManager.MyEvent += new MyDelegate(GetName);
                //绑定事件处理方法方式二
                personManager.MyEvent += GetName;
                //绑定事件处理方法方式三(匿名方法)
                personManager.MyEvent += delegate (string name) { Console.WriteLine("My name is " + name); };
                personManager.Execute("Atomy");
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        4.5 C#控件中的事件

        在C#控件中存在很多的事件,比如Click、TextChanged、SelectIndexChanged等等,很多都是通过EventHandler委托绑定事件的处理方式,EventHandler

    可说是C#控件中最常见的委托 。

        public delegate void EventHandler (Object sender, EventArgs e)

        EventHandler委托并无返回值,sender代表引发事件的控件对象,e代表由该事件生成的数据 。

        下面代码演示C#控件中的事件绑定:

        public partial class EventTest : Form
        {
            public EventTest()
            {
                InitializeComponent();
            }
    
            private void EventTest_Load(object sender, EventArgs e)
            {
                btnEvent.Click += new EventHandler(btnEvent_onclick);
            }
    
            public void btnEvent_onclick(object sender,EventArgs e)
            {
                Button button = (Button)sender;
                MessageBox.Show(button.Text);
            }
        }
    View Code

        运行结果如下:

        EventHandler只是EventHandler<TEventArgs>泛型委托的一个简单例子。事实上,大家可以利用 EventHandler<TEventArgs> 构造出所需要的委托。

        public delegate void EventHandler<TEventArgs> (Object sender, TEventArgs e)

        在EventHandler<TEventArgs>中,sender代表事件源,e代表派生自EventArgs类的事件参数。开发人员可以建立派生自EventArgs的类,从中加入需要使用

    到的事件参数,然后建立EventHandler<TEventArgs>委托。

        下面的例子中,先建立一个派生自EventArgs的类MyEventArgs作为事件参数,然后在EventManager中建立事件myEvent , 通过Execute方法可以激发事件。

    最后在测试中绑定myEvent的处理方法ShowMessage,在ShowMessage显示myEventArgs的事件参数Message。

        class Program
        {
            #region EventArgs派生
            public class MyEventArgs : EventArgs
            {
                private string args;
    
                public MyEventArgs(string message)
                {
                    args = message;
                }
    
                public string Message
                {
                    get { return args; }
                    set { args = value; }
                }
            }
    
            public class EventManager
            {
                public event EventHandler<MyEventArgs> myEvent;
    
                public void Execute(string message)
                {
                    myEvent?.Invoke(this, new MyEventArgs(message));
                }
            }
    
            public static void ShowMessage(object obj, MyEventArgs e)
            {
                Console.WriteLine(e.Message);
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region EventArgs派生
                EventManager eventManager = new EventManager();
                eventManager.myEvent += new EventHandler<MyEventArgs>(ShowMessage);
                eventManager.Execute("How are you?");
                Console.Read();
                #endregion
            }
        }
    View Code

        运行结果如下:

        4.6 为用户控件建立事件

        开发过程中,往往会出现很多类似的控件与代码,开发人员可以通过用户控件来避免重复的代码。但往往同一个用户控件,在不同的页面中需要有不同的响应。

    此时为用户控件建立事件,便可轻松地解决此问题。

        新建一个Person类:

        class Person
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public int Age { get; set; }
        }
    View Code

        新建一个用户控件,拖入一个DataGridView,命名为:DataGridViewControl。

        public partial class DataGridViewControl : UserControl
        {
            public DataGridViewControl()
            {
                InitializeComponent();
            }
    
            private void DataGridViewEvent_Load(object sender, EventArgs e)
            {
                dataGridView1.DataSource = GetPersonList();
            }
    
            //数据源
            private IList<Person> GetPersonList()
            {
                IList<Person> list = new List<Person>();
                Person person = new Person
                {
                    Id = 1,
                    Name = "Hello",
                    Age = 1
                };
                list.Add(person);
                person = new Person
                {
                    Id = 2,
                    Name = "World",
                    Age = 2
                };
                list.Add(person);
                return list;
            }
    
            //单元格事件
            public event DataGridViewCellEventHandler CellEventHandler;
    
            private void dataGridView1_RowEnter(object sender, DataGridViewCellEventArgs e)
            {
                CellEventHandler?.Invoke(sender, e);
            }
        }
    View Code

        新建一个WinForm窗体,命名为Main,并拖入DataGridViewControl用户控件。

        public partial class Main : Form
        {
            public Main()
            {
                InitializeComponent();
            }
    
            private void Main_Load(object sender, EventArgs e)
            {
                dataGridViewControl1.CellEventHandler += DataGridViewControl1_CellEventHandler;
            }
    
            private void DataGridViewControl1_CellEventHandler(object sender, DataGridViewCellEventArgs e)
            {
                DataGridView dataGridView1 = (DataGridView)sender;
                int rowIndex = int.Parse(e.RowIndex.ToString());
                int columnIndex = int.Parse(e.ColumnIndex.ToString());
                MessageBox.Show(dataGridView1.Rows[rowIndex].Cells[columnIndex].Value.ToString());
            }
        }
    View Code

        运行结果如下:

        使用控件已有的事件固然简单,但它限制了传送的参数类型,使开发人员无法传送额外的自定义参数。在结构比较复杂的用户控件中,使用已有的控件事件,

    显然不够方便,此时,您可以考虑为用户控件建立自定义事件。

        新建一个MyEventArgs类:

        public class MyEventArgs : EventArgs
        {
            public string Name { get; set; }                    //买家姓名
            public string Address { get; set; }                 //买家地址
            public string Tel { get; set; }                     //买家电话
            public string OrderCode { get; set; }               //订单号码
            public IList<OrderItem> OrderItemList { get; set; } //订单明细
            public MyEventArgs(string name, string address, string tel, string orderCode, IList<OrderItem> orderItemList)
            {
                Name = name;
                Address = address;
                Tel = tel;
                OrderCode = orderCode;
                OrderItemList = orderItemList;
            }
        }
    View Code

        新建一个OrderItem类:

        public class OrderItem
        {
            public string OrderItemID { get; set; } //明细单ID
            public string Goods { get; set; }       //商品名称
            public double Price { get; set; }       //商品单价
            public int Count { get; set; }          //商品数量
    
            public OrderItem(string id, string goods, double price, int count)
            {
                OrderItemID = id;
                Goods = goods;
                Price = price;
                Count = count;
            }
        }
    View Code

        新建一个用户控件,拖入一个DataGridView及一个Button,命名为:OrderControl。

        public partial class OrderControl : UserControl
        {
            //自定义委托及事件  
            public delegate void MyDelegate(object sender, MyEventArgs myEventArgs);
            public event MyDelegate MyEvent;
    
            public OrderControl()
            {
                InitializeComponent();
            }
    
            private void OrderControl_Load(object sender, EventArgs e)
            {
                dataGridView1.DataSource = GetList();
            }
    
            //数据源
            private IList<OrderItem> GetList()
            {
                IList<OrderItem> list = new List<OrderItem>();
                OrderItem orderItem = new OrderItem("1", "鼠标", 160, 2);
                list.Add(orderItem);
                orderItem = new OrderItem("2", "键盘", 80, 2);
                list.Add(orderItem);
                return list;
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                if (MyEvent != null)
                {
                    if (dataGridView1.CurrentCell != null)
                    {
                        int rowIndex = int.Parse(dataGridView1.CurrentCell.RowIndex.ToString());
                        MyEventArgs myEventArgs = new MyEventArgs
                        (
                            "张三",
                            "广东",
                            "111111111111",
                            "2019122300001",
                            GetList()
                        );
                        MyEvent(this, myEventArgs);
                    }
                }
            }
        }
    View Code

        新建一个WinForm窗体,命名为OrderForm,并拖入OrderControl用户控件。

        public partial class OrderForm : Form
        {
            public OrderForm()
            {
                InitializeComponent();
            }
    
            private void OrderForm_Load(object sender, EventArgs e)
            {
                orderControl1.MyEvent += OrderControl1_MyEvent;
            }
    
            private void OrderControl1_MyEvent(object sender, MyEventArgs myEventArgs)
            {
                MessageBox.Show($"Name={myEventArgs.Name},Address={myEventArgs.Address},Tel={myEventArgs.Tel}","信息",MessageBoxButtons.OK,MessageBoxIcon.Information); 
            }
        }
    View Code

        运行结果如下:

  • 相关阅读:
    TextBox 只有下划线
    can't find web control library(web控件库)
    DropDownListSalesAC”有一个无效 SelectedValue,因为它不在项目列表中。
    IDE、SATA、SCSI、SAS、FC、SSD 硬盘类型
    如何打印1px表格
    CSS控制打印 分页
    Virtual Server could not open its emulated Ethernet switch driver. To fix this problem, reenable the Virtual Server Emulated Et
    Xml中SelectSingleNode方法中的xpath用法
    热带水果莫入冰箱?水果存放冰箱大法
    探索Asp.net的Postback机制
  • 原文地址:https://www.cnblogs.com/atomy/p/12075999.html
Copyright © 2011-2022 走看看