发信人: mengy (LEAR DLLS 命令时,将从内存中清除), 信区: BorlandDev
标 题: delphi属性和控件编辑器
发信站: 哈工大紫丁香 (2001年03月13日18:45:01 星期二), 转信
属性和控件编辑器 <<上一篇 >>下一篇
摘自:Delphi www 大全
译者:
Delphi提供了开放的API,是程序员可以增强Delphi IDE的功能。共有4种开放工具
的APIs:属性编辑器、控件编辑器、专家/导航和版本控制系统。本文讨论属性编辑
器和控件编辑器,给出的例子说明如何写自己的Delphi属性、控件编辑器。
属性编辑器
属性编辑器是Delphi IDE的扩展。这听起来非常复杂和困难,但是实际上是很简单
的。我们可以为枚举类型构造一个属性编辑器。记得TForm的颜色属性吗?当我们
想改变它的值,看到了下拉框中列出了所有的可选值。那就是枚举类型的属性编辑
器,我们也同样能做到,只需要几行代码,没什么特别的。注意到程序员并没有写
一个属性编辑器,而是通知Delphi使用枚举类型的属性编辑器,为它的枚举特别定
义的。
现有的属性编辑器
在我们搞清楚属性编辑器到底内部是什么之前,先看看Delphi中已有的。开始一个
新工程,在implementation中加入"uses DsgnIntf;"编译,打开browser查找
TPropertyEditor(只要输入TPrope):
如果没算错的话,在DSGNINTF中注册了至少21个客户属性编辑器(custom property
editors),注意:事实上,还有更多的属性编辑器在其他单元中,例如C:
\DELPHI\LIB\PICEDIT.DCU.中的TPictureEditor。
TPropertyEditor
对象察看器为所有的属性提供缺省的编辑。我们可以使用不同的方法重载这种行为
,来使用特别的属性编辑器(21种预制的属性编辑器都扩充了对象察看器来处理其
属性)。那么,究竟是怎样工作的呢?它是起源一个基类,我们必需重载已达到我
们的目的。五个新的Delphi 2.0的方法-其中三个是变量相关的-在编译开关
{$IFDEF WIN32}中一保证一下代码在所有的delphi版本中适用。
Type
TPropertyEditor = class
protected
function GetPropInfo: PPropInfo;
function GetFloatValue: Extended;
function GetFloatValueAt(Index: Integer): Extended;
function GetMethodValue: TMethod;
function GetMethodValueAt(Index: Integer): TMethod;
function GetOrdValue: Longint;
function GetOrdValueAt(Index: Integer): Longint;
function GetStrValue: string;
function GetStrValueAt(Index: Integer): string;
{$IFDEF WIN32}
function GetVarValue: variant;
function GetVarValueAt(Index: Integer): variant;
{$ENDIF}
procedure Modified;
procedure SetFloatValue(Value: Extended);
procedure SetMethodValue(const Value: TMethod);
procedure SetOrdValue(Value: Longint);
procedure SetStrValue(const Value: string);
{$IFDEF WIN32}
procedure SetVarValue(const Value: variant);
{$ENDIF}
public
destructor Destroy; override;
procedure Activate; virtual;
function AllEqual: Boolean; virtual;
procedure Edit; virtual;
function GetAttributes: TPropertyAttributes; virtual;
function GetComponent(Index: Integer): TComponent;
function GetEditLimit: Integer; virtual;
function GetName: string; virtual;
procedure GetProperties(Proc: TGetPropEditProc); virtual;
function GetPropType: PTypeInfo;
function GetValue: string; virtual;
procedure GetValues(Proc: TGetStrProc); virtual;
procedure Initialize; virtual;
{$IFDEF WIN32}
procedure Revert;
{$ENDIF}
procedure SetValue(const Value: string); virtual;
{$IFDEF WIN32}
procedure ValueAvailable: Boolean;
{$ENDIF}
property Designer: TFormDesigner read FDesigner;
property PrivateDirectory: string read GetPrivateDirectory;
property PropCount: Integer read FPropCount;
property Value: string read GetValue write SetValue;
end;
TPropertyEditor编辑对象察看器中一个或是一串控件的一个属性。属性编辑器根
据属性的类型而被创建,由RegisterPropertyEditor注册的类型决定。稍候有一个
指示程序员如何使用这些工程的例子。所有的published属性都将出现在对象察看
器中,当设计者进行读写属性的值时,其属性编辑器(为这种属性类型的)将被使用
。
在以下的时间里,我们将只注意方法中的需要被重载的重要部分,属性编辑器的行
为。
GetAttributes
这是最重要的方法,他决定了属性编辑器的类型和行为。有三种属性编辑器(除了
缺省的编辑框):下拉框(我们在前面提到过的),分属性列表和对话框。
GetAttributes返回TPropertyAttributes类型,包含了一下内容:
paValueList:属性编辑器能返回属性的枚举列表。如果GetValues调用过程附带值
,这个属性必需设置。这将使在对象察看其中的属性的右边出现下拉按钮。
paSubProperties: 属性编辑器有子属性时,将在当前属性下方显示成标准的大纲
格式。如果GetProperties产生属性对象时这个属性必需设置。
paDialog:表示这个编辑方法将产生对话框。这将在对象察看其中的属性右边出现
'...'按钮。
paSortList: 对象察看器将把GetValues返回的列表按照字母排序。
paAutoUpdate: 每当编辑发生改变是调用SetValue方法,而不是改变别提交时。例
如Caption属性。
paMultiSelect: 允许多个控件被选择时显示属性的值。有些属性不适合多选的情
况。例如Name属性。
paReadOnly: 属性值不允许改变。
GetValue:返回属性的串值,缺省时返回'(unknown)',这应该被重载以返回适当
的值。
GetValues:当GetAttributes返回paValueList时被调用。它必须为每一个属性所
接受的值调用参数函数。TEnumProperty将在列举中传递所有的参数。
SetValue(Value):设置属性的值。属性编辑器必须能够知道调用哪一个
SetXxxValue函数。如果字符串不是合适的格式或不是合法的值,属性编辑器应该
产生一个例外,描述产生的问题。SetValue可以忽略所有的改变,允许通过Edit方
法编辑所有的属性。例如Picture属性。
Edit
当'...'按钮被安下或是属性被连击识别调用。这样,例如弹出一个对话框,通过
更有效的方法,而不是简单的文本来编辑属性。例如Font属性。
TFileNameProperty
使用这几个重要的方法我们就能写出自己的属性编辑器了:为filename建立一个大
卡文件对话框属性编辑器。我们得记住编写控件从本质来说是非可视化的任务,写
书信编辑器并不复杂。我们需要制定一个我们说想要的'Dialog'类型,所以我们在
GetAttributes中返回[paDialog]。然后,我们在Edit过程中处理,这次包含一个
TOpenDialog来找到任何存在的文件。
unit FileName;
interface
uses
SysUtils, DsgnIntf;
Type
TFileNameProperty = class(TStringProperty)
public
function GetAttributes: TPropertyAttributes; override;
procedure Edit; override;
end;
procedure Register;
implementation
uses
Dialogs, Forms;
function TFileNameProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog]
end {GetAttributes};
procedure TFileNameProperty.Edit;
begin
with TOpenDialog.Create(Application) do
try
Title := GetName; { name of property as OpenDialog caption }
Filename := GetValue;
Filter := 'All Files (*.*)|*.*';
HelpContext := 0;
Options := Options + [ofShowHelp, ofPathMustExist,
ofFileMustExist];
if Execute then SetValue(Filename);
finally
Free
end
end {Edit};
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(TFileName),nil, '',
TFileNameProperty)
end;
end.
注意到我们调用属性编辑器的GetName函数来得到属性的名字。
属性编辑器需要注册过程(register)在delphi中来注册它本身(确切的说是在
delphi应用程序中)。我们可以只为一个控件注册属性编辑器,也可以我所有的相
同类型的属性注册。例如上面的例子TFileNameProperty就是为所有的控件做的。
当然,属性编辑器必需安装了并且首先注册。
为了在Register过程中注册,我们需要调用RegisterPropertyEditor。它有4个参
数:第一个是属性类型的类型信息的指针。这里,我们使用内置的函数TypeInfo。
第二个是这个编辑器应用的控件类型,如果为nil,这个编辑器为所有控件的所有
给定的类型的属性。这里,我们希望属性编辑器为所有的控件的TFileName类型工
作。所以只需要把第二个参数置为nil。第三个参数时属性的名字,这个参数只有
在第二个参数指定了控件的类型的情况下才有作用。同样,我们把它置为空字符串
。第四个参数属性编辑器的自己的类型,这里是TFileNameProperty。
安装属性编辑器和安装控件类似。这里,属性编辑器有自己的注册过程(不失为某
个控件的属性编辑器,而是某个属性的)。一般来说,如果一个属性编辑器是为特
别控件的特别属性,最好和控件一起注册。现在,我们只要把带有
TFileNameProperty的单元FILENAME加到控件版中(delphi 1:使用Options |
Install Components,Delphi 2中使用Component | Install。
安装之后,在任何控件的TFileName类型的属性,我们可以看到省略号,这表明对
话框的属性编辑器已在这个属性中安装了。
如果点击省略号,导致Delphi 2弹出如下对话框。
只用了几行代码,我们就写出了为所有控件的TFileName类型的属性的TFileName属
性编辑器。这仅仅是个例子,展示了属性编辑器在编写Delphi控件和程序的巨大潜
能。
在我们研究下一个例子之前,来看看TPropertyEditor其他可以别重载的方法:
Activate
这个方法在属性被选中时别调用。这可能有用,决定某些属性被选中时的行为。只
有GetAttributes返回paSubProperties和paMultiSelect时,才学要准确的控制。
AllEqual
当超过一个控件被选中时别调用。如果这个方法返回true,调用GetValue,否则在
对象察看其中显示空白。只有在GetAttributes返回paMultiSelect时被调用。
GegComponent
返回属性编辑器的控件的索引。当项获得控件时要用倒它。只有在GetAttributes
返回paMultiSelect时,属性编辑器才能处理多个控件。
GetEditLimit
返回使用这可以输入的值得字符串的个数,对象察看其内置的编辑器对这有限制,
缺省值为255。
GetName
返回属性的名字,缺省时值时从类型信息中得到的。如果属性的值和对象察看其中
所显示的不一样时才有必要重载。
GetProperties
应该在被编辑的属性的每一个子属性时重载,调用PropertyProc,并为每一个子属
性传递一个新的TPropertyEditor。缺省时,假定没有子属性,PropertyProc不别
调用。TClassProperty将为每一个published属性传递一个新的属性编辑器。
TSEtProperty为每一个元素传递一个新的编辑器。
GetPropType
返回被编辑的属性的类型信息的指针。
Initialize
由属性编辑器创建之后,使用之前调用。属性编辑器经常被创建,但因为不是整个
选择的公用属性而被抛弃,Initialize只有在对象察看器使用时,而不是被抛弃属
性编辑器时调用。
以下是创建新的TPropertyEditor类其他非常有用的属性和方法
Name 属性
GetName返回的属性的名称。
PrivateDirectory 属性
是.exe或Delphi.ini指定的工作目录(Working Directory),如果属性编辑器需要
辅助程序或是状态文件(模版、例子等),他们应保存在这个目录中。
Properties indexed property
TProperty代表了所有被属性编辑器编辑的控件,如果不只有一个控件,每个控件
都有创建的相应的TProperty,一般来说,用不着它,因为Get/SetXxxValue方法能
适当的处理它。
Value属性
作为字符串,GetValue返回属性的当前值。
Modified
调用它来指示属性的值是否改变了。SetXxxValue方法会自动调用,如狗我们直接
调用SetXxxValue,我们必须同样调用Modified。
GetXxxValue
得到属性中的一个属性的值。调用TRroperty适当的GetXxxValue方法来得到相应的
值。
SetXxxValue
设置所有属性的值。调用TRroperty适当的GetXxxValue方法来得到相应的值。
TPicture 属性编辑器
好了,我们已经明白如何时属性编辑器的行为像对话框,只是我想起了
Delphi中最令人急躁的属性编辑器:图形、图标、图像的picture编辑器。并不是
它不工作,而是他并不友好。如果我们点击了Load按钮,在对话框中选这所需要的
文件。问题是,在关闭对话框之前,我们无法看到文件中的内容。返回Picture编
辑器,决定是否适合要求,所以我们呢不得不一次又一次的点击Load按钮。这在我
们现在许多小文件中查找时特别令人恼火。
我们需要预览功能,看看目录种的图形文件中的图像,这对我来说是一个新的
属性编辑器(Borland公司没有提供PICEDIT.DCU的源代码,PICEDIT.DFM通用没用,
所以我们只能写自己的Picture编辑器,而不是增强现有的。
TImageForm
首先,我们的实际想要的对话框或表单,我已经设计了如下所示的一个,右下角显
示了所选择的文件的图形,根据我们的需要,甚至可以对图像进行拉伸(对小图像
没什么价值,对大图形就有效了)。
Win31:
Win95:
TPictureEditor
现在我们有了表单来选择图形,来看看如何让它在属性编辑器中工作。首先我
们需要看看GRAPHIC.PAS来搞清楚什么图形、图像在第一个位置存在。我们受到
TPersistert的两个继承所限制,TPicture和TGraphic,这用一来,我们只注意.
bmp文件。只增强TPicture和TBitmap类,这意味着我们想为TPicture和TBitmap提
供新的图形属性编辑器。
unit PictEdit;
interface
uses
DsgnIntf;
Type
TPictureEditor = class(TClassProperty)
public
function GetAttributes: TPropertyAttributes; override;
procedure Edit; override;
end;
procedure Register;
implementation
uses
SysUtils, Controls, Graphics, TypInfo, ImageFrm;
function TPictureEditor.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog]
end {GetAttributes};
procedure TPictureEditor.Edit;
begin
with TImageForm.Create(nil) do
try
ImageDrBob.Picture := TPicture(GetOrdValue);
if ShowModal = mrOk then
begin
if (GetPropType^.Name = 'TPicture') then
SetOrdValue(LongInt(ImageDrBob.Picture))
else { Bitmap }
SetOrdValue(LongInt(ImageDrBob.Picture.Bitmap))
end
finally
Free
end
end {Edit};
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(TPicture), nil, '',
TPictureEditor);
RegisterPropertyEditor(TypeInfo(TBitmap), nil, '', TPictureEditor)
end;
end.
注意到我们并不想让TPictureEditor属于任何特别的控件,我们只有自己注册、安
装,如同其他自定义的控件、专家,使用options|install components...对话框
,在重新编译控件库之后(记住先备份!),我们得到了为每一个TPicture(TImage中
的)和TBitmap(在TSpeedButton和TBitBtn)得到了新的Picture编辑器。
最重要的是,已有了为TPictures和TBitmaps的属性编辑器:Borland提供的名为
picture的编辑器。如果我们用自己的名字会不会有麻烦呢?不会的。因为最后注
册的特别的控件和属性编辑器将精确的重载上一个。例如,我们在装一个
TBitmaps的属性编辑器,将覆盖我们刚才所安装的。这次,我们用增强了的
TPictureEditor覆盖缺省的Borland的Picture编辑器。
好了,我们已经看了仅有的几种属性编辑器,我们特别讨论了paDialog属性编辑器
。从我个人的观点来看,这是最容易的定制开发者在设计时输入属性值的方法。还
有很多种方法来写属性编辑器,但我只能写到这里了。你可以自己察看
TPropertyEditor类。下面,我们讲述Component Editor--控件编辑器。
Component Editors
控件编辑器和属性编辑器类似,都是用来增强Delphi的开发环境。和属性编辑器一
样,他们是一个简单的类的继承,并且要重载和重定义一些方法来达到你想要的目
的
当然,和属性编辑器不同的是控件编辑器编辑的是控件,而不是属性。它限制在特
别的控件中,通常是在鼠标右击控件时运行(设计时),这种行为和属性编辑器大
不相同,但另一个方面,编写控件编辑器的工程和属性编辑器很相似。
控件编辑是根据控件类型,为每一个设计表单上的控件创建的(参看DSGNINTF.
PAS中的GetComponentEditor和RegisterComponentEditor)。当控件被双击,
edit方法被调用。但控件的相关菜单被调用,GetVerbCount和GetVerb方法被调用
来建立菜单。如果一个动作被选择,调用ExecuteVerb。但控件被粘贴到剪贴板,
调用Paste方法。只用当我们想要在想光彩当中加入新的动作,改变鼠标双击的行
为,或者是粘贴额外的剪贴板格式时,我们才需要创建控件编辑器。
基类TComponentEditor的类型定义在DSGNINTF.PAS,如下(适合于所有的delphi版
本):
Type
TComponentEditor = class
private
FComponent: TComponent;
FDesigner: TFormDesigner;
public
constructor Create(AComponent: TComponent;
ADesigner: TFormDesigner); virtual;
procedure Edit; virtual;
procedure ExecuteVerb(Index: Integer); virtual;
function GetVerb(Index: Integer): string; virtual;
function GetVerbCount: Integer; virtual;
procedure Copy; virtual;
property Component: TComponent read FComponent;
property Designer: TFormDesigner read FDesigner;
end;
控件编辑器程序员可以重载6个虚拟的方法(和属性编辑器相比较简单的多)
Create(AComponent, ADesigner)
调用它来创建控件编辑器。AComponent是编辑器要编辑的控件,ADesigner是设计
者找到控制和创建方法的界面(不常用)。
Edit
单控件被双击时别调用,控件编辑器可以弹出对话框来响应这个动作,或者是一些
设计指南。如果GetVerbCount比0大,edit会调用列表中的第一个动作。
ExecuteVerb(Index)
使用相关菜单的动作索引。这意味着这是由控件编辑器决定的。
GetVerb
属性编辑器应该返回在相关菜单中显示的字符串。放置&和...字符时控件编辑器的
责任。
GetVerbCount
为GetVerb和ExecuteVerb提供的合法的数值,这个值从0开始计数(0...
GetVerbCount - 1)。
copy
单控件被粘贴到剪贴板时被调用。控件的文件图形已经在剪贴板中了。这个了控件
编辑器一个改变类型格式的机会,设计者会忽略,但其他程序可以识别。
缺省的控件编辑器
除了通常的控件编辑器,还有其他的控件编辑器在许多控件中使用(除非安装了其
他控件编辑器重载了缺省的)。TDefaultEditor
Apart from the general type TComponentEditor, there is also a default
component editor that is used by most components (unless another
component editor is installed to override the default one). The
TDefaultEditor default component editor implements Edit to search the
properties of the component and to generate (or navigate to an already
existing) the OnCreate, OnChange or OnClick event, whichever it finds
first, or just the first alphabetic event handler which is available.
Type
TDefaultEditor = class(TComponentEditor)
private
FFirst: TPropertyEditor;
FBest: TPropertyEditor;
FContinue: Boolean;
procedure CheckEdit(PropertyEditor: TPropertyEditor);
protected
procedure EditProperty(PropertyEditor: TPropertyEditor;
var Continue, FreeEditor: Boolean); virtual;
public
procedure Edit; override;
end;
当控件编辑器改变控件时,它必须调用Designer.Modfied方法告知设计者控件所属
的表单经改变了。Designer是TFormDesigner类型的属性,可以从
TComponentEditor基类中得到。任何控件编辑器都可以和设计者交流(至少要告诉
设计者控件已经被修改了),如果我们只是用控件编辑器来显示一些信息(如产生
一个about对话框),就没有必要通知设计者。
定制控件编辑器Custom Component Editor
当编写定制的控件时,一般考虑几个可能的基类,所有vcl的类都以TObject为根,
TObject类包含了Create和Destroy方法,用于创建和注销类的一个实例。
TPersistent类,从Tobject继承,包含了从表单文件中读写属性的方法。
TComponent是所有控件父类,因为它包含的属性和方法允许Delphi使用
TComponent作为设计单元,通过对象查看器查看他们的属性,并把这些控件放置在
控件面板上。
TObject
+- TPersistent
+- TComponent
+- TControl
+- TGraphicControl
+- TWinControl
+- TCustomControl
如果我们想创建一个新的非可视化的控件,就须要从TComponent继承。可视化控件
类应该从TControl类继承。因为它包含了可视化设计控件的基本功能,象位置、可
视性、字体和标题。从TControl类继承的是TCraphicControl和TWinControl,两者
的区别在于TWinControl包含了实际的窗口句柄而TCraphicControl没有。这样,标
准的Windows控制从TWinControl继承,而TBevel,TImage,TSpeedButton和
TShape则从TCraphicControl继承,最后,TCustomControl与TWinControl
TGraphicControl都相似(???)既然缺省的控件编辑器能产生事件句柄的基本
代码,象OnCreate,OnChange,OnClick事件,我们首先看没有或不需要这样事件
的控件,不需要窗口句柄的控件,例如不需要输入焦点,非可视化控件从
Tcomponent继承。
其它控件编辑器需要控制的控件,是通用对话框控件。例如TOpenDialog,
TSaveDialog,ColorDialog,PrintDialog和PriterSetupDialog,FontDialog,
FindDialog,RePlaceDialog已经有一些事件,所以不适合(当要写一个事件句柄
时,缺省的控件编辑器将把我们带到代码编写器中),这五个有趣的对话框没有事
件,缺省的控件编辑器也没有缺省的行为。
那么这些对话框的控件编辑器意味着什么?
当你设置TOverDialog所有的属性,你是否要知道[你所改变的是什么样子]?我们
只有运行程序,这样才能运行对话框,能否简单一些,只要双击对话框就能预览其
状态?好,我想可以,以下部分是如何解决这个问题。
我们只要重载TComponentEdit类的edit方法,看看运行的是什么TComponent,然后
运行它。
procedure TCommonDialogComponentEditor.Edit;
begin
if (Component IS TOpenDialog) then { also TSaveDialog }
(Component AS TOpenDialog).Execute
else
if (Component IS TPrintDialog) then
(Component AS TPrintDialog).Execute
else
if (Component IS TPrinterSetupDialog) then
(Component AS TPrinterSetupDialog).Execute
else
if (Component IS TColorDialog) then
(Component AS TColorDialog).Execute
else
inherited Edit { default behaviour }
end {Edit};
注意“inherited Edit”是必须的,这样才能确保不是TComponent时缺省的控件编
辑器行为能保证执行。
注册控件编辑器,如同定制控件及属性编辑器,设置比注册属性编辑器更简单,因
为我们调用RegisterComponentEditor只需要两个参数,第一个参数是控件编辑器
所需控件的名字(类型),第二个是参数是控件编辑器本身的类型。
安装控件编辑器同样和安装控件和属性编辑器相似,只需加入到我们的控件库中,
就可以在设计时执行Execute方法!控件编辑器可以为单独的类创建,也可以为一
套类(包括所有的继承),这里,这个控件编辑器为TCommomDialog的一套控件而
设置的。
unit CompEdit;
{ TCommonDialogComponentEditor version 0.1 }
interface
uses
DsgnIntf;
Type
TCommonDialogComponentEditor = class(TComponentEditor)
public
procedure Edit; override;
end;
procedure Register;
implementation
uses
Dialogs;
procedure TCommonDialogComponentEditor.Edit;
begin
if (Component IS TOpenDialog) then { also TSaveDialog }
(Component AS TOpenDialog).Execute
else
if (Component IS TPrintDialog) then
(Component AS TPrintDialog).Execute
else
if (Component IS TPrinterSetupDialog) then
(Component AS TPrinterSetupDialog).Execute
else
if (Component IS TColorDialog) then
(Component AS TColorDialog).Execute
else
inherited Edit { default behaviour }
end {Edit};
procedure Register;
begin
{ register TCommonDialogComponentEditor for
TCommonDialog and all its derived classes }
RegisterComponentEditor(TCommonDialog, TDialogEditor)
end;
end.
安装后,拖一个TOpenDialog到表单中,连击它,看看吧!嘿嘿
不幸的是,我们测试TFontDialog时,缺省的行为(编辑OnApply事件)并没有发生
,我们确实调用了inherited Edit方法,但这是TComponentEditor.editor方法,
而不是缺省的控件编辑器(TDefaultEditor)的Edit方法。有两种方法补救:一种
是只为TCommonDialog的5种继承的控件安装,或者是从TDefaultEditor继承,而不
是从TComponentEditor类继承。
菜单控件编辑器 Menu Component Editor
控件编辑器不是只能作一件事(在Edit中所作的),实际上,我们能创建自己的弹
出菜单来提供几个选择,让我们再来作通用对话框的‘Execute/Preview’,但这
次加入一些信息显示。为了避免打断TCommonDialog继承类的缺省行为,我们只注
册TCommonDialogComponentEditor到5个没有缺省事件属性的类,这回我们要重载
两个函数和一个过程:函数GetVerbCount、GetVerb和过程ExecuteVerb。
TCommonDialogComponentEditor = class(TComponentEditor)
public
function GetVerbCount: Integer; override;
function GetVerb(Index: Integer): string; override;
procedure ExecuteVerb(Index: Integer); override;
end;
因为有两个菜单选项,一个是运行Dialog.Excute,另一个是显示“About”信息,
函数GetverbCount应该返回2。
function TCommonDialogComponentEditor.GetVerbCount: Integer;
begin
GetVerbCount := 2
end {GetVerbCount};
我们知道Delphi总是从0开始记数的,另一个函数,GetVerb的Index参数可能得到
0和1两种可能的菜单输入选择。在0时我们应返回‘Execute’,在1(或其它值)
应返回‘About’
function TCommonDialogComponentEditor.GetVerb(Index: Integer):
string;
begin
if Index >= 1 then GetVerb := '&About...'
else GetVerb := '&Execute...'
end {GetVerb};
为了显示是哪一个通用对话框,我可以作如下改写:
GetVerb := '&Execute ' + Component.ClassName + '...'
最后,我们要执行动作,在0时,和以前在TDialogEditor中一样,在比0大时显示
对话框
procedure TCommonDialogComponentEditor.ExecuteVerb(Index: Integer);
begin
if index >= 1 then
MessageDlg('TCommonDialogComponentEditor (c) 1996 by Dr.Bob',
mtInformation, [mbOk], 0)
else
if (Component IS TOpenDialog) then { also TSaveDialog }
(Component AS TOpenDialog).Execute
else
if (Component IS TPrintDialog) then
(Component AS TPrintDialog).Execute
else
if (Component IS TPrinterSetupDialog) then
(Component AS TPrinterSetupDialog).Execute
else
if (Component IS TColorDialog) then
(Component AS TColorDialog).Execute;
end {ExecuteVerb};
注意,可以使用Compent属性来得到确实的控件
试试吧,能得到如下的弹出式菜单
如果我们执行TColorDialog,选择另一种颜色,关闭对话框,在对象查看器中还是
原来的值,为什么没有更新?
实际上,我们忘记调用了Designer.Modified 方法来通知设计者(包括对象查看器
)控件已经改变了,例如某个属性的值改变了。
全部代码如下:
unit CompMenu;
{ TCommonDialogComponentEditor version 0.5 }
interface
uses
DsgnIntf;
Type
TCommonDialogComponentEditor = class(TComponentEditor)
function GetVerbCount: Integer; override;
function GetVerb(index: Integer): String; override;
procedure Executeverb(index: Integer); override;
end;
procedure Register;
implementation
uses
Dialogs;
function TCommonDialogComponentEditor.GetVerbCount: Integer;
begin
GetVerbCount := 2
end {GetVerbCount};
function TCommonDialogComponentEditor.GetVerb(index : Integer):
String;
begin
if Index >= 1 then GetVerb := '&About...'
else GetVerb := '&Execute...'
end {GetVerb};
procedure TCommonDialogComponentEditor.ExecuteVerb(index: Integer);
begin
if index >= 1 then
MessageDlg('TCommonDialogComponentEditor (c) 1996 by Dr.Bob',
mtInformation, [mbOk], 0)
else
if (Component IS TOpenDialog) then { also TSaveDialog }
(Component AS TOpenDialog).Execute
else
if (Component IS TPrintDialog) then
(Component AS TPrintDialog).Execute
else
if (Component IS TPrinterSetupDialog) then
(Component AS TPrinterSetupDialog).Execute
else
if (Component IS TColorDialog) then
(Component AS TColorDialog).Execute;
Designer.Modified { inform the Object Inspector of the change }
end {Edit};
procedure Register;
begin
RegisterComponentEditor(TCommonDialog,
TCommonDialogComponentEditor)
end;
end.
菜单缺省控件编辑器Menu Default Component Editors
现在我们知道了如何为没有缺省行为的TCommonDialogs写一个新的控件编辑器,如
何扩展有缺省行为的,但是又不丢失其原有的行为?例如,提供弹出菜单第一项(
缺省)选择OnEvent转向代码编写,第二项预览执行,第二项选择信息显示。为了
做到这一个,我们需要另一个从TDefaultEditor继承的
TCommomDialogDefaultEditor类。同样我们要重载两个函数和一个过程。
TCommonDialogDefaultEditor = class(TDefaultEditor) { not
TComponentEditor }
public
function GetVerbCount: Integer; override;
function GetVerb(Index: Integer): string; override;
procedure ExecuteVerb(Index: Integer); override;
end;
现在要3个选项,一个是缺省动作(TDefaultEditor.Edit),一个为Dialog.
Excute,一个显示信息。所以GetVerbCount应返回3。
function TCommonDialogDefaultEditor.GetVerbCount: Integer;
begin
GetVerbCount := 3
end {GetVerbCount};
GetVerb的Index参数能得到0,1,2三个可能值,在0时返回‘OnEvent’handler
code’,在1时返回‘Exeute TxxxDialog’,2时返回其它值)返回‘About’
function TCommonDialogComponentEditor.GetVerb(Index: Integer):
string;
begin
case Index of
0: GetVerb := '&OnEvent handler code';
1: GetVerb := '&Execute ' + Component.ClassName + '...';
else GetVerb := '&About...'
end
end {GetVerb};
最后,执行动作。
procedure TCommonDialogComponentEditor.ExecuteVerb(Index: Integer);
begin
if index >= 2 then
MessageDlg('TCommonDialogDefaultEditor (c) 1996 by Dr.Bob',
mtInformation, [mbOk], 0)
else
if index = 1 then
begin
if (Component IS TFindDialog) then { also TReplaceDialog }
(Component AS TFindDialog).Execute
else
if (Component IS TFontDialog) then
(Component AS TFontDialog).Execute;
Designer.Modified
end
else inherited Edit { TDefaultEditor.Edit for index = 0 }
end {ExecuteVerb};
我想,你已经明白了。如果安装且激活了新的TCommonDialog DefaultEditor ,在
TFindDialog 时,可得到如下弹出式菜单:
如果选择第一项,双击控件,这一般是执行菜单动作的第一项),自动地弹到代码
编辑器中,一切都如你所愿,代码如下:
unit CompMenu;
interface
uses
DsgnIntf;
Type
TCommonDialogComponentEditor = class(TComponentEditor)
function GetVerbCount: Integer; override;
function GetVerb(index: Integer): String; override;
procedure Executeverb(index: Integer); override;
end;
TCommonDialogDefaultEditor = class(TDefaultEditor)
function GetVerbCount: Integer; override;
function GetVerb(index: Integer): String; override;
procedure Executeverb(index: Integer); override;
end;
procedure Register;
implementation
uses
Dialogs;
{ TCommonDialogComponentEditor }
function TCommonDialogComponentEditor.GetVerbCount: Integer;
begin
GetVerbCount := 2
end {GetVerbCount};
function TCommonDialogComponentEditor.GetVerb(index : Integer):
String;
begin
if Index >= 1 then GetVerb := '&About...'
else GetVerb := '&Execute ' + Component.ClassName +
'...'
end {GetVerb};
procedure TCommonDialogComponentEditor.ExecuteVerb(index: Integer);
begin
if index >= 1 then
MessageDlg('TCommonDialogComponentEditor (c) 1996 by Dr.Bob',
mtInformation, [mbOk], 0)
else
begin
if (Component IS TOpenDialog) then { also TSaveDialog }
(Component AS TOpenDialog).Execute
else
if (Component IS TPrintDialog) then
(Component AS TPrintDialog).Execute
else
if (Component IS TPrinterSetupDialog) then
(Component AS TPrinterSetupDialog).Execute
else
if (Component IS TColorDialog) then
(Component AS TColorDialog).Execute;
Designer.Modified
end
end {ExecuteVerb};
{ TCommonDialogDefaultEditor }
function TCommonDialogDefaultEditor.GetVerbCount: Integer;
begin
GetVerbCount := 3
end {GetVerbCount};
function TCommonDialogDefaultEditor.GetVerb(index : Integer): String;
begin
case Index of
0: GetVerb := '&OnEvent handler code';
1: GetVerb := '&Execute ' + Component.ClassName + '...';
else GetVerb := '&About...'
end
end {GetVerb};
procedure TCommonDialogDefaultEditor.ExecuteVerb(index: Integer);
begin
if index >= 2 then
MessageDlg('TCommonDialogDefaultEditor (c) 1996 by Dr.Bob',
mtInformation, [mbOk], 0)
else
begin
if (Component IS TFindDialog) then { also TReplaceDialog }
(Component AS TFindDialog).Execute
else
if (Component IS TFontDialog) then
(Component AS TFontDialog).Execute;
Designer.Modified
end
else inherited Edit { TDefaultEditor.Edit }
end {ExecuteVerb};
procedure Register;
begin
{ empty default component editors }
RegisterComponentEditor(TOpenDialog, TCommonDialogComponentEditor);
RegisterComponentEditor(TPrintDialog,
TCommonDialogComponentEditor);
RegisterComponentEditor(TPrinterSetupDialog,
TCommonDialogComponentEditor);
RegisterComponentEditor(TColorDialog,
TCommonDialogComponentEditor);
{ event default component editors }
RegisterComponentEditor(TFontDialog, TCommonDialogDefaultEditor);
RegisterComponentEditor(TFindDialog, TCommonDialogDefaultEditor)
end;
end.
结论:
在本文中,我们讨论了如何使用属性编辑器和控件编辑器来扩展和增强Delphi设计
环境,我们知道了如何使用已有的编辑器,更重要的是,怎样写自己的,属性编辑
器和控件编辑器只是所谓OpenTools API 的头两个,甚至不是最重要的,但是确实
是非常有益。从这里开始吧!
如果你有什么好的资料,可以寄给我哟:) <<回到首页 <<上一篇 >>下一篇
------------------------------------------------------------------------
--------
Delphi 技巧集
Copyright 1999.11 by 东子 Mail to me!
感谢广州视窗提供主页空间
--
大海无边天做岸
山登绝顶我为风
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.227.121]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:209.457毫秒