`

事件的基本用法

阅读更多

事件的基本用法
事件的基本用法

我在对RO例子 Variants 跟踪时有了个意外收获,有种茅塞顿开。
提起事件,我也用过,一般都是控件上提供啥事件我就用啥了。
用的最多的就是OnClick事件,至于为啥这样用,我不知道,
稀里湖途地我用了快小半辈子了。

我从下面的代码开始跟踪的:
procedure TVariantsClientMainForm.FormCreate(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to ComponentCount - 1 do
    if (Components[i] is TROMessage) then begin
      rgMessageType.Items.AddObject(TROMessage(Components[i]).Name, Components[i]);
    end;

  rgMessageType.ItemIndex := 1;
  FVariantsService := (RORemoteService as IVariantsService);
end;

在 rgMessageType.Items.AddObject(TROMessage(Components[i]).Name, Components[i]);上
下了一个断点,单步跟踪,当我跟踪到下面代码时:
procedure TStringList.Changed;
begin
  if (FUpdateCount = 0) and Assigned(FOnChange) then
    FOnChange(Self);
end;

当执行FOnChange(Self);时程序无形中地跳到了下面代码的位置:

procedure TCustomRadioGroup.ItemsChange(Sender: TObject);
begin
  if not FReading then
  begin
    if FItemIndex >= FItems.Count then FItemIndex := FItems.Count - 1;
    UpdateButtons;
  end;
end;

呀!这咋这么奇怪呢?TStringList.Changed 一执行程序就会跳到TCustomRadioGroup.ItemsChange这来
这是为啥呢?在这里我郁闷了很久。啥东西的作用始得可以在两个类之间方法的跳来跳去的呢?
于是,我试图往深里跟踪FOnChange(Self);它想到里面看看是咋回事,可是单步不进去呀,一个单之后
就直接跳到TCustomRadioGroup.ItemsChange这里了。我反复地跟踪,很是郁闷,这是为啥呢?

从语法上分析一下FOnChange(Self);
FOnChange: TNotifyEvent;
FOnChange 定义的是一个事件。
TNotifyEvent = procedure(Sender: TObject) of object;

啥意思?定义成这样的事件就可以类之间的方法跳来跳去的吗?
它咋知道从TStringList跳到TCustomRadioGroup呢?它咋不往别的类上跳去呢?
我估计肯定是有啥说法,不可能是瞎乱跳的吧。

我更郁闷了,我垂头又丧气,这是为啥呢?

我突然想起了Items定义的是 TString而当执行rgMessageType.Items.AddObject
AddObject执行的确是TStringlist下的AddObject,因为在TCustomRadioGroup.create中
FItems是这样创建的:FItems := TStringList.Create;

我合计肯定是得有事件赋值了.
我打开TCustomRadioGroup.create下一看还真是这样:
TStringList(FItems).OnChange := ItemsChange;

我明白了事件为啥会跳来跳去的了,原来桥是在这搭起来的.


整体看一下TStringList类

TStringList = class(TStrings)
  private
...
  FOnChange: TNotifyEvent;
..FOnChanging: TNotifyEvent;.
protected
    procedure Changed; virtual;
    procedure Changing; virtual;
...
 public
...
property OnChange: TNotifyEvent read FOnChange write FOnChange;

end;

事件就是这么用,可能就是上面的这样的结构用法.有如下特点:
1、事件看上去就是一个属性,所以在类中添加事件的方法与添加属性是一样的。
   如:property OnChange: TNotifyEvent read FOnChange write FOnChange;

2、添加事件需要定义属性与之相关的域,该域用于存储事件引用的实际方法指针。
   如:FOnChange: TNotifyEvent;
   FOnChange 域为方法指针,在读取OnChange属性时将调用该指针指向的方法;
   而给OnChange属性赋值时,所赋的值(方法地址)将写到FOnChange域中,由它保存
   方法的入口地址
 如,在TCustomRadioGroup.create中有如下语句:
   TStringList(FItems).OnChange := ItemsChange;
  经过这样的赋值之后FOnChange域保存的ItemsChange方法的入口地址。
  也就是说TCustomRadioGroup下的ItemsChange方法的指针。也就是说该方法的首地址。
  所以执行FOnChange(Self);它实际上就是执行ItemsChange方法了。

3、还需要声明一个响应数据域变化的方法
   如:procedure Changed; virtual;
       procedure Changing; virtual;

   看看它是怎么实现的:
procedure TStringList.Changed;
begin
  if (FUpdateCount = 0) and Assigned(FOnChange) then
    FOnChange(Self);
end;
数据域在变化前的事件。
procedure TStringList.Changing;
begin
  if (FUpdateCount = 0) and Assigned(FOnChanging) then
    FOnChanging(Self);
end;

Assigned 方法判断FOnChange域是否已经赋值了,也就是说是否指定了某个方法,
如果没有指定,则其值为nil,否则程序通过FOnChange方法指针域调用具体的方法。

跟据VCL的惯例,有人把这叫夹心面包,皮萨饼,确实在VCL中能看到很多这样的手法。
看看下面的代表,Changing;和Changed;中间夹着一些代码。

procedure TStringList.InsertItem(Index: Integer; const S: string; AObject: TObject);
begin
  Changing;
  if FCount = FCapacity then Grow;
  if Index < FCount then
    System.Move(FList^[Index], FList^[Index + 1],
      (FCount - Index) * SizeOf(TStringItem));
  with FList^[Index] do
  begin
    Pointer(FString) := nil;
    FObject := AObject;
    FString := S;
  end;
  Inc(FCount);
  Changed;
end;

变化前变化后所要触发的事件。


不知道这是不是事通的一般通常的用法,可能是,原来事件是这么个用法。

TStringList(FItems).OnChange := ItemsChange;
结过这么一赋值之后

FOnChanging(Self); 就相当于执行:
相当于调用 TCustomRadioGroup类下的ItemsChange 方法

说明某个类的方法指针域可以具有指向其他类的方法的值
属于方法指针类型,用于处理事件。

 

分享到:
评论

相关推荐

    委托的基本用法介绍项目

    Unity 项目中委托Delegate的用法,项目案例。 Chinar用简单的案例,帮您理解委托的基本用法,注册事件机制问题

    UIAlertView的基本用法与UIAlertViewDelegate对对话框的事件处理方法

    ### UIAlertView的基本用法 创建一个UIAlertView实例首先需要导入`UIKit`框架: ```swift import UIKit ``` 然后,你可以通过初始化方法来创建一个UIAlertView对象: ```swift let alert = UIAlertView(title: ...

    flex4自定义事件用法

    通常,我们会选择`flash.events.Event`或`mx.events.FlexEvent`作为基类,前者适用于基本事件,后者则为Flex框架提供了一些额外的属性和方法。 - 自定义事件类应包含一个构造函数,用于初始化事件的属性。例如,...

    cropper基本用法.pdf

    《使用Cropper实现图形剪切的基本方法》 在网页开发中,图像处理是一项常见的需求,尤其是在用户上传照片或需要自定义裁剪图片时。Cropper是一款强大的JavaScript图像裁剪库,它提供了丰富的功能和易用的API,使得...

    JavaScirpt的基本用法

    在本主题中,我们将深入探讨JavaScript的基本语法、事件处理以及window对象。 首先,我们来了解一下JavaScript的基本语法。JavaScript语法与C++和Java有相似之处,但更宽松,它允许在一行内编写多条语句。变量声明...

    ButterKnife 控件事件基本实现demo

    这个“ButterKnife 控件事件基本实现demo”是一个很好的学习资源,可以帮助开发者理解并掌握ButterKnife的基本用法。 ButterKnife的核心功能包括View注入和事件注入: 1. **View注入**:传统方式中,我们需要在...

    Java Swing的基本用法

    Java Swing是Java GUI(图形...综上所述,Java Swing的基本用法涵盖了组件的使用、布局管理、事件处理等多个方面,是构建Java桌面应用不可或缺的技能。通过学习和实践,开发者能够创建出功能丰富、用户友好的图形界面。

    Delphi TServerSocket和TClientSocket两个组件的基本用法

    通过上述介绍,我们可以了解到 `TServerSocket` 和 `TClientSocket` 在 Delphi 中的使用方法及其基本原理。这两种组件能够帮助开发者快速构建出基于 Socket 协议的应用程序,实现客户端与服务器之间的数据交互。在...

    C#基本控件用法(winform,原创)

    C#基本控件用法(winform) C#中的控件用法是非常重要的,尤其是在winform应用程序中。今天,我们将来学习一些基本的控件用法,包括Button、CheckBox、ComboBox、DataGridView、ListView、TreeView和MdiForm等。 ...

    Android开发实现Fragment监听返回键事件功能的方法

    前面的文章Android开发教程之Fragment定义、创建与使用方法详细讲述了Fragment的基本概念与用法。这里再来分析一下Fragment监听返回键事件的具体应用。 背景 项目要求用户注册成功后进入修改个人资料的页面,且不...

    第二天android项目 基本空间的用法

    在Android开发过程中,基本空间的用法是构建用户界面(UI)的基础,这对于任何应用程序都是至关重要的。在“第二天android项目 基本空间的用法”中,我们将会探讨如何有效地利用各种UI元素,如按钮(Button)和菜单...

    用户控件的基本用法(属性、方法、事件、委托)

    在实际项目中,用户控件的属性、方法、事件和委托的合理使用,可以极大地提高代码的复用性和可维护性。通过创建用户控件,开发者可以构建出复杂而定制化的界面元素,同时保持代码的清晰和结构化。在设计用户控件时,...

    Android ListView控件基本用法

    Android ListView 控件基本用法 Android ListView 控件是一种基础控件,主要用于展示列表数据。在 Android 开发中,ListView 控件是最常用的控件之一。下面我们将详细介绍 Android ListView 控件的基本用法。 一、...

    Delphi ListView基本用法大全

    下面将详细介绍Delphi中ListView的基本用法。 1. **ListView组件的基本结构** - **Items**: 这是ListView的主要数据存储,每个Item代表列表中的一行。每个Item可以有多个SubItems,用于显示不同的列数据。 - **...

    matlab +gui基本用法视频

    MATLAB GUI的基本用法视频教程涵盖了创建GUI、设计界面元素、处理用户事件等多个方面,对于初学者或希望提高MATLAB GUI编程技能的人来说是极有价值的资源。 在MATLAB GUI中,你可以通过 GUIDE(Graphical User ...

    C++ Builder DrawGrid控件基本用法,使用实例,一个例子(诚诚照片Demo)

    在这个"诚诚照片Demo"中,我们将探讨DrawGrid控件的基本用法,通过实例来学习如何在C++ Builder中有效地使用它。 首先,要在C++ Builder项目中添加DrawGrid控件,你需要打开工具箱,并将DrawGrid控件拖放到窗体上。...

    UITableViewController最基本用法实现(Accessory多选、自定义Style多选、目录表格、编辑表格)(一)

    同时,可以使用`UITableViewDelegate`的`didSelectRowAt`方法来监听选中事件。 其次,自定义Style多选通常涉及到自定义单元格。`UITableViewCellStyle`提供了多种预设样式,但有时我们可能需要自定义单元格以满足...

    Leaflet基本用法

    总结来说,Leaflet是一个强大的开源地图库,它的基本用法包括初始化地图、加载底图、添加标记和信息弹窗、图层控制以及事件监听。而Cesium则提供了3D地图功能,两者结合可以构建出更丰富的Web地图应用。在实际开发中...

    JavaScript onclick事件使用方法详解

    本文将深入探讨`onclick`事件的工作原理、使用方法以及几个实用示例。 `onclick`事件是一个内联事件处理器,它可以绑定到任何可点击的HTML元素,如按钮、链接或图像等。当用户在浏览器中对这些元素进行点击操作时,...

    jquery 基本用法小例子

    本教程将通过一系列小例子来讲解 jQuery 的基本用法,包括事件处理、DOM 操作、选择器、动画效果等核心概念。** ### 1. **选择器** jQuery 提供了丰富的选择器,使得选取 HTML 元素变得非常简单。例如: - `$("#id...

Global site tag (gtag.js) - Google Analytics