深入浅出话事件(下)

2023-05-16

深入浅出话事件(下)

二.事件的由来

       在传统的面向对象的概念中是没有“事件”这个概念的。传统的面向对象概念中只有数据(Data,也称为field、域、成员变量)和方法(Method,也就是成员函数、function)。如果我没记错,那么事件这个概念最早出现在微软的COM技术中,又因为VB是基于ActiveXCOM的一种)的,所以“事件”这一概念便通过VB广而推之、为众多程序员所熟知并使用的——我就是其中的一员。

       .NET Framework实际上是对COM的更高层级的封装——要知道,早先.NET Framework这个名字没有出来之前,它叫“COM 3 来着——自然就保留了对事件的支持。

三.事件的意义

       《进化论》说:“物竞天择,合理即存在。”

       微软说:“我是老大,存在即合理!”

       姑且不管微软是不是在耍大牌、搞霸权——事件的存在的确给程序的开发带来了很多方便。从设计层面上讲,它使程序在逻辑思维方面变得简洁清晰、便于维护;从技术层面上讲,它把坚涩难懂的Windows消息传递机制包装的漂漂亮亮,极大地降低了程序员入职的门槛儿。

从软件工程的角度上来看,事件是一种通知机制,是类与类之间保持同步的途径。

问曰:什么同步?

答曰:消息同步!

四.事件的本质——对消息传递的封装

事件可以被激发(Fire,也有称为“引发”的),一个类所包含的成员事件可以在多种情况下被激发。最典型的:一个按钮的Click事件,可以由用户使用鼠标来激发它,也可以由测试这个软件的另一个软件通过Win32 API函数来激发它。

我们来简要讨论一下这个Click事件:

其实,如果你了解Win32的本质,你应该明白用户是不可能直接接触到某个控件的。表面上看,的确是用户用鼠标点击了一下按钮。而实际上,当用户按下鼠标左键的时候是通过鼠标向Windows操作系统发送了一个“左键单击[x,y]点”消息,然后Windows再根据[x,y]的位置把这个消息分配(路由)给应该接收它的控件——这就是Windows的消息传递/路由机制。

同理,当你移动鼠标的时候,看似好像指针在随你的意愿移动,而实际上是你的鼠标在以每秒钟几百次的频率把当前位置汇报给Windows操作系统,然后Windows再把一个漂亮的指针“画”在屏幕上给你看——哈哈,我们都被骗了!

然而这些内容对于C#程序员都是不可见的——都被封装成了“事件”。因此,从Windows系统的机理上讲,事件机制就是对Windows消息传递机制的包装。

       下面的代码是对Visual Studio 2005自动生成的WinForm程序的一个模拟。读懂之后,大家可以自己写一个WinForm,对照剖析其中的机理。

代码:

//============水之真谛============//
//                                                                  //
//          http://blog.csdn.net/FantasiaX       //
//                                                                 //
//========
上善若水,润物无声=========//

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms; //
先添加对System.Windows.FormsSystem.Drawing程序集的引用哦!
using System.Drawing;

namespace EmulateWinForm
{
    //
自定义的EmulateForm类,派生自Form类。
    class EmulateForm : Form
    {
        //
两个控件
        private Button myButton;
        private TextBox myTextBox;

        //
初始化各个控件和窗体本身,并把控件加入窗体的Controls数组。
        private void InitializeComponent()
        {
            myButton = new Button();
            myTextBox = new TextBox();

            myButton.Location = new System.Drawing.Point(195, 38);
            myButton.Size = new System.Drawing.Size(75, 23);
            myButton.Text = "Click Me";
            myButton.Click += new EventHandler(myButton_Click);//
挂接事件处理函数

            myTextBox.Location = new System.Drawing.Point(12, 12);
            myTextBox.Size = new System.Drawing.Size(258, 20);

            Controls.Add(myButton);
            Controls.Add(myTextBox);
            Text = "EmulateForm";
        }

        //myButton
Click事件发生时,EmulateForm类给予的事件响应函数(Event Handler
        void myButton_Click(object sender, EventArgs e)
        {
            myTextBox.Text = "Hello, Event World!";
        }

        //
EmulateForm类的构造函数中执行上面的初始化方法
        public EmulateForm()
        {
            InitializeComponent();
        }
    }

    class Program
    {
        static void Main (string[] args)
        {
            EmulateForm myForm = new EmulateForm();
            Application.Run(myForm);
        }
    }
}
代码剖析:

1.        要想引用using System.Drawing; using System.Windows.Forms;这两个Namespace,首先要手动添加对System.DrawingSystem.Windows.Forms两个程序集(Assembly)的引用。

2.        EmulateForm类是自定义的,注意,它派生自Form类。为了清晰起见,我已经把代码简化到了几乎最简……只有两个成员变量。myButtonButton类的一个实例;myTextBoxTextBox类的一个实例。EmulateForm类的成员方法private void InitializeComponent()完全是对真正WinForm程序的模仿,在它的函数体里,对成员变量进行了实例化、初始化(比如确定大小和位置),并将它们加入了窗体的Controls数组里。这个函数将在EmulateForm的构造函数里被执行。

3.        本例中最重要的部分就是对myButton的初始化。注意这句:myButton.Click += new EventHandler(myButton_Click);
myButton.Click
myButtonClick事件,你可能会奇怪:这回怎么不用自己去声明一个事件了呢?呵呵,因为.NET Framework已经为我们准备好了这个事件,你直接用就好了。不过,我们是为了探寻底细而来,所以我还得仔细说一说这个事件。

4.        详细剖析Button.Click事件:首先,事件都是基于委托的,那么myButton.Click事件是基于哪个委托呢?通过查找MSDN,你可以发现myButton.Click是继承自Control类,并基于EventHandler这一委托——下面是EventHandler委托的声明

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

如果你不太了解事件是怎么声明的,回过头去温习一下《深入浅出话事件(上)》。
在这个声明中,方括号中的是Attribute,你暂时不用去理会它。关键是看EventHandler这个委托:这个委托的参数列表要求它所挂接的函数(对于事件来说就是挂接的事件处理函数)应该具有两个参数——Object类型的senderEventArgs类型的e这两个参数起什么作用呢?呵呵,其实非常好玩儿——前面说过了,事件机制是对消息机制的封装,你可以把消息理解成一枚炮弹,sender就是“谁发射的炮弹”,e就是“炮弹里装的什么东西”,炮弹的目标当然就是消息的接收处理者了。我们仔细回顾一下上篇亲手写的那个FireEventArgs类:这个类里不是有两个成员变量吗?一个是代表着火楼层的floor,一个是代表火级的fireLevel,随着Building类实例的FireAlarmRing事件引发,FireEventArgs类的实例e就被发射到了Employee类和Fireman类的实例那里,这两个实例再打开“炮弹”根据发射过来的内容给出相应处理。就像真实的战争中的炮弹有常规弹、穿甲弹、燃烧弹等等一样,我们的“消息炮弹”也不只一种,信手拈来几个与大家共赏一下:
EventArgs类:这个就是Click事件中使用的那个。算是常规弹吧。因为用户点击按钮是个非常简单的事件,不需要它携带更多的信息了。
MouseEventArgs类:是由MouseMoveMouseUpMouseDown事件发射出来。它的实例携带了很多其它的信息,其中最常用的就是一个X和一个Y——用腿肚子想也能想明白,那是鼠标当前的位置。后面的例子中我们给出演示。
PaintEventArg类:由Paint事件发送出来。这颗炮弹可不简单,那些非常漂亮的自定义控件都离不开它!在它的肚子里携带有一个Graphics,代表的是你可以在上面绘画的一块“画布”……
OK
,先列举3个吧MSDN里有它们的全家福,位置是System.EventArgsDerived Classes树。微软在.NET Framework方面可谓下足了功夫,从这些Event Args(事件参数),到各种委托,再到五花八门的事件,都已经为我们做了良好的封装,我们只需要拿出来用就是了。

5.        void myButton_Click(object sender, EventArgs e)EmulateForm类对myButton.Click事件的响应函数(也称事件处理器,Eventhandler)。注意它的参数列表,是不是与EventHandler委托一致啊:p

6.        主程序没什么好说的了——new一个EmulateForm的实例出来,用Application.Run方法执行程序就好了。

7.        顺便在这里做一个纠偏:上面已经解释过sender是什么了——它是消息的发送者。我屡次在一些书中发现诸如“事件发送者”这类的话,这是不对的!你想啊,事件只能引发、激发、发生,怎么可能“发送”呢?不合逻辑……

作业1

      

       建立一个WinForm程序,如图。包含1Panel3TextBox1Button

要求:

1.    当鼠标在Panel里滑动时,textBox1textBox2分别显示鼠标当前的XY

2.    当鼠标点击按钮时,textBox3要显示Hello Events World!字样。

提示:

1.    留心MouseMove事件的e

2.    留心Visual Studio 2005使用的是C# 2.0,并且使用partial关键字将Form1类的代码分别存储在了Form1.csForm1.Designer.cs两个文件里。

作业2

       将《深入浅出话事件(上)》中嘎子炸鬼子的程序升级至使用事件的版本。(代码我将在以后的日子里给出)。

 

OVER

 

法律声明:本文章受到知识产权法保护,任何单位或个人若需要转载此文,必需保证文章的完整性(未经作者许可的任何删节或改动将视为侵权行为)。若您需要转载,请务必注明文章出处为CSDN以保障网站的权益;请务必注明文章作者为刘铁猛,并向bladey@tom.com发送邮件,标明文章位置及用途。转载时请将此法律声明一并转载,谢谢!

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入浅出话事件(下) 的相关文章

  • keil5 制作自己的pack

    转发自 xff1a http blog sina com cn s blog dc9571b90102vhqf html 以前都是用的keil5以下版本的keil xff0c 没有RTE这个功能 xff0c 后来安装了keil5以上的版本
  • 找出带环单向链表的环入口(交点)

    其实这个问题已经被问烂了 xff0c 但是之前没有想透 xff0c 今天算是解决得差不多 找环的入口这个问题 xff0c 其实是建立在另外一个问题之上的 判断单向链表是否有环 土方法很多 xff0c 但是比较好的目前就那么一个 xff1a
  • 关于我最近看的一本书——大名鼎鼎的APUE

    APUE xff0c Know as Unix环境高级编程 xff0c 我每天都在用自己的绳命去看 xff0c 每天都燃烧自己去看 什么样的书 xff0c 一看就是上乘之中的珍稀之品 xff1f 这本 不同于不少机械工业出版社的大部头 xf
  • 写博客加分不

    写第一篇博客 xff0c 就看看加分不 xff0c 这个网站分很重要 xff0c 不然下不了东西
  • 若在Ubantu中查询IP地址输入ifconfig时显示没找到该命令时怎么办?

    新手在学习Linux中想要查询IP地址输入ifconfig时却显示如下图 xff1a 这时我们只需要输入sudo apt install net tools 显示这下图时就证明已经安装好了 这时我们只需要 输入ifcogfig xff0c
  • 小觅相机深度版运行Vins-mono

    首先声明 xff0c 本人自己也是slam新手 xff0c 此贴只因为自己在用小觅相机深度版运行Vins的时候太过无助 xff0c 所以想写个自己运行出结果的完整过程 xff0c 仅供参考 xff0c 如有不对之处 xff0c 还望不吝指教
  • shell如何判定C/C++程序是否执行成功

    linux编程中经常遇到这样的问题 xff0c 即判断一个程序执行是否成功 xff0c 通常实现方法是通过进程的退出状态来判断 xff0c 当linux的一个进程执行完成后会返回一个进程的退出状态 xff0c 通过判断退出状态码 可以确定该
  • 删除数组中的指定元素或数组对象

    1 删除数组中的某个指定元素 1 xff09 首先获取元素下标 xff0c 用indexOf 函数找到他的位置index xff0c 如果没有找到这个元素那么index将会等于 1 xff1b 如下为找到元素2的下标 var array 6
  • stm32cubemx 配置FreeRTOS相关基础基础知识及串口接收中断实验

    本实验首先大家自身要有stm32cubemx基础配置 xff0c 比如GPIO 中断 串口 SPI等 xff0c FreeRTOS有相关的调用函数基础及调度任务的概念都需要提前理解 单独stm32cubemx或FreeRTOS网上很多 xf
  • select()

    select 简述 xff1a 确定一个或多个套接口的状态 xff0c 如需要则等待 include lt winsock h gt int PASCAL FAR select int nfds fd set FAR readfds fd
  • C#工控上位机开发

    对于电源软件开发者来说 xff0c 上位机的开发难度是远远小于下位机的 xff0c 之前几个月我一直在研究电力电子技术和下位机的控制算法 xff0c 也有了一点点的收获 xff0c 但说实话还是差的太远了 xff0c 而且人力物力资源非常稀
  • 虚拟机运行gazebo卡

    操作系统 xff1a ubuntu18 04 链接 B站链接 xff1a Autolabor初级教程 ROS机器人入门 问题 虚拟机gazebo卡的话 xff0c 可以试试在虚拟机设置里打开3d加速 xff0c 在每次启动gazebo前命令
  • 谨以此文献给才毕业一两年的朋友

    谨以此文献给才毕业一两年的朋友 选自同事信件 谨以此文献给才毕业一两年的朋友我们终于进入了这个社会 从此结束了被学校老师看管的生涯 xff0c 结束了做父母乖宝贝的日子 xff0c 也结束从父母兄长那里拿钱的幸福时光 我们从家里搬了出来 x
  • winform怎样设置comboBox默认值

    combox是开发winform常用的组件之一 xff0c 如何添加他的默认值呢 xff01 方法 步骤 新建一个windows窗体应用程序 xff0c 这里项目命名为test01 在默认打开的Form1中 xff0c 在左边的工具箱拖拉两
  • Internal server error 500 问题解决思路

    我们系统在一次升级之后 xff0c 生产环境大量出现Internal server error 500错误 xff0c 具体场景 xff1a 在APP上使用拍照功能后 xff0c APP通过Http协议上传压缩后的照片到服务端 xff0c
  • c# List集合类常用操作:二、增加

    所有操作基于以下类 class Employees public int Id get set public string Name get set public string City get set public DateTime Bi
  • 485通讯协议_终于有人把RS485通讯协议应用及缺点分析清楚了,看完收获多多

    RS 485是工业控制环境中常用的通信协议 xff0c 具有抗干扰能力强 传输距离长的特点 RS 485通信协议是对RS 232协议的改进 协议层不变 xff0c 但只有物理层得到了改进 xff0c 从而保留了串行通信协议应用简单的特点 R
  • 常用串口调试工具比较(详细)

    目前有许多免费的串口调试工具 xff0c 比较常用的有 xff1a 1 友善串口调试助手 xff08 v2 6 5 xff09 优点 xff1a 1 xff09 使用方便 xff0c 不丢包 xff1b 2 xff09 串口自动识别 xff
  • 请问在Delphi中用什么方式打开窗体能让它一直在最前面,而且还可以对其它窗体进行操作?

    将要打开的窗口FormStyle属性设置为 fsStayOnTop xff0c 然后再用show方法打开窗口

随机推荐

  • 微软Surface Pro 4/5平板如何重装Win10系统?教程分享

    重装Win10系统很多用户都会 xff0c 但是如果是平板 xff0c 那么操作起来和电脑可能会有不同 xff0c 毕竟平板需要触屏的支持 xff0c 今天我们要讲的是微软Surface Pro 4 5如何重装Win10系统 xff0c 主
  • C#发送16进制串口数据

    个困扰两天的问题 xff1a 需要通过串 向设备发送的数据 xff1a 0A010 7e 08 00 11 00 00 7e 76 7f 我先将每个16进制字符转换成10进制 xff0c 再将其转换成ASCII码对应的字符 lt summa
  • C# WinForm遍历窗体控件的3种方法

    这篇文章主要介绍了C WinForm遍历窗体控件的3种方法 xff0c 帮助大家更好的理解和使用c xff0c 感兴趣的朋友可以了解下 目录 1 循环遍历2 递归遍历3 使用反射 1 循环遍历 1 2 3 4 5 6 7 8 private
  • gazebo_ros:未找到命令

    操作系统 xff1a ubuntu18 04 链接 B站链接 xff1a Autolabor初级教程 ROS机器人入门 问题 gazebo ros xff1a 未找到命令 解决办法 sudo apt install ros span cla
  • 【深入学习51单片机】一、基于8051的RTOS内核任务切换堆栈过程剖析

    我一直在写裸机 xff0c 写的多了自然会对rtos产生浓厚兴趣 xff0c 最有意思的莫过于任务切换了 xff0c 可以在多个死循环里面跳转 xff0c 很神奇的样子 本文学习参考程序是网上一个基于8051的简易os xff0c 从哪里下
  • 如何使用网络调试助手调试UDP

    最近的一个项目需要使用UDP xff0c 在网上下载了一个网络调试助手的小工具进行调试 xff0c 非常方便 xff0c 在这里简单的向大家介绍一下它的使用方法 xff0c 仅供参考 xff0c 其他类似的调试工具都差不多 工具 原料 网络
  • Delphi 回调函数及例子

    Delphi回调函数 1 回调函数的概述 回调函数是这样一种机制 xff1a 调用者在初始化一个对象 xff08 这里的对象是泛指 xff0c 包括OOP中的对象 全局函数等 xff09 时 xff0c 将一些参数传递给对象 xff0c 同
  • 解决Window10连接共享目录 登录失败:未知的用户名或错误密码 的解决方法

    关于未知的用户名或错误密码 win10共享这个很多人还不知道 xff0c 今天菲菲来为大家解答以上的问题 xff0c 现在让我们一起来看看吧 xff01 1 按 Win 43 R 运行 执行 窗口 xff0c 输入 gpedit msc 命
  • 教您电脑电源短接哪两根线风扇转

    如果哦我们没有接电脑主板 xff0c 如何让电脑电源风扇转动 我们只有通过电脑电源线的短接来解决这个问题 xff0c 但是电脑电源线哪两跟线使风扇转动呢 一起跟小编来看看吧 电脑电源短接哪两根线风扇转 xff1a 1 电脑电源本身是一个供电
  • 编译PX4固件

    PX4编译 文章目录 PX4编译疑难杂症bug1bug2catkin build isolated 官方脚本Step1Step2 安装常用依赖Step3 创建并运行脚本Step4 补全代码子模块Step5 验证仿真 官方offboard 例
  • ubuntu18.04安装mavros并读取pixhawk飞控数据

    Mavros源码安装 最新mavros详细安装教程 xff08 亲测可行 xff09 启动mavros 上述步骤完成后 xff0c 启动px4 launch roslaunch mavros px4 launch 此时rostopic li
  • 相机标定:相机模型和畸变模型

    一 相机标定方案 相机内参标定是确定内参和畸变参数 equidistqant畸变模型 或者 radial tangential模型 的过程 本文首先介绍SLAM中常用的相机模型和畸变模型 xff0c 随后介绍我们采用的两种内参标定方案 xf
  • 新手入门Docker之Windows下如何使用Docker

    Docker是什么 xff1f Docker 将应用程序与该程序的依赖 xff0c 打包在一个文件里面 运行这个文件 xff0c 就会生成一个虚拟容器 程序在这个虚拟容器里运行 xff0c 就好像在真实的物理机上运行一样 有了 Docker
  • C/C++服务器开发常见面试题(一)

    C C 43 43 服务器开发常见面试题 转自 xff1a LinuxC C 43 43 服务器开发面试题 一 编程语言 1 根据熟悉的语言 xff0c 谈谈两种语言的区别 xff1f 主要浅谈下C C 43 43 和JAVA语言的区别 1
  • Realsense D455/435内参标定以及手眼标定

    相机的内外参 内参数 与相机自身特性有关的参数 xff0c 焦距 xff0c 像素大小外参数 xff0c 相机的位置 xff0c 旋转方向 为什么要内参标定 理想情况下 xff0c 镜头会将一个三维空间中的直线也映射成直线 xff08 即射
  • 最新Ubuntu20.04安装指南(防踩坑版)

    文章目录 最新Ubuntu20 04安装 配置指南 防踩坑版 一 备份 1 Windows系统镜像 2 个人资料 3 一些杂项 二 启动盘UEFI引导1 启动盘制作2 设置以U盘方式启动 三 Ubuntu安装 最新Ubuntu20 04安装
  • 18.Python爬虫之Scrapy框架

    scrapy 框架 01 Scrapy 链接02 Scrapy 的爬虫流程03 Scrapy入门04 setting py文件中的常用设置4 1 logging模块的使用4 2 61 61 scrapy项目中的setting py常用配置内
  • 树莓派上手实战之SSH下配置VNC服务器实现远程桌面(可以实现开机自启动vncserver)

    VNC是linux下面常用的远程桌面 xff0c 用它可以在windows或者unix主机上方便的通过网络操作远程主机而不需要一个额外的显示器 xff0c 非常实用 这篇教程将会详细讲解安装配置VNC服务器开启远程桌面服务的全部过程 首先
  • N2N(docker方式安装)实现远程访问家里群晖

    N2N xff08 docker 安装 xff09 实现远程访问家里群晖 前言部署阿里云ECS安装n2n supernode 及 edge 节点在黑群晖上安装 edge节点windows 安装edge节点 测试遇到的问题检查tun模块状态测
  • 深入浅出话事件(下)

    深入浅出话事件 xff08 下 xff09 二 xff0e 事件的由来 在传统的面向对象的概念中是没有 事件 这个概念的 传统的面向对象概念中只有数据 xff08 Data xff0c 也称为 field 域 成员变量 xff09 和方法