📄 028.htm
字号:
<HTML><HEAD><meta http-equiv="Content-Type" content="text/html; charset=GB2312"><TITLE>-->DELPHI专题--入门技巧-->属性和控件编辑器</TITLE>
<META NAME="keywords" CONTENT=" DELPHI专题--入门技巧 属性和控件编辑器">
<META NAME="description" CONTENT=" - DELPHI专题--入门技巧 - 属性和控件编辑器">
<style>
<!--
#page {position:absolute; z-index:0; left:0px; top:0px}
.tt3 {font: 9pt/12pt "宋体"}
.tt2 {font: 12pt/15pt "宋体"}
a {text-decoration:none}
a:hover {color: blue;text-decoration:underline}
-->
</style>
</HEAD>
<body text="#000000" aLink=#9900ff link=#006699 vLink=#006699 bgcolor="#FFFFFF" leftmargin="3" topmargin="3" marginheight="3" marginwidth="3">
<a href="index.html">返回</a>
<TABLE WIDTH="100%" CELLPADDING=10 CELLSPACING=0 BORDER=0>
<TR>
<TD class="tt2" bgcolor="#F5F8F8" width="84%"><center><B><FONT style="FONT-SIZE: 16.5pt" COLOR="#FF6666" FACE="楷体_GB2312">属性和控件编辑器</FONT></B></center>
<hr color="#EE9B73" size="1" width="94%">
<p>
<br>
Delphi提供了开放的API,是程序员可以增强Delphi IDE的功能。共有4种开放工具的APIs:属性编辑器、控件编辑器、专家/导航和版本控制系统。本文讨论属性编辑器和控件编辑器,给出的例子说明如何写自己的Delphi属性、控件编辑器。<p><span
class="p2">属性编辑器<br>
属性编辑器是Delphi IDE的扩展。这听起来非常复杂和困难,但是实际上是很简单的。我们可以为枚举类型构造一个属性编辑器。记得TForm的颜色属性吗?当我们想改变它的值,看到了下拉框中列出了所有的可选值。那就是枚举类型的属性编辑器,我们也同样能做到,只需要几行代码,没什么特别的。注意到程序员并没有写一个属性编辑器,而是通知Delphi使用枚举类型的属性编辑器,为它的枚举特别定义的。</span></p>
<p><span class="p2">现有的属性编辑器</span></p>
<p><span class="p2">在我们搞清楚属性编辑器到底内部是什么之前,先看看Delphi中已有的。开始一个新工程,在implementation中加入"uses
DsgnIntf;"编译,打开browser查找TPropertyEditor(只要输入TPrope):</span></p>
<p align="center"> </p>
<p><span class="p2">如果没算错的话,在DSGNINTF中注册了至少21个客户属性编辑器(custom
property editors),注意:事实上,还有更多的属性编辑器在其他单元中,例如C:\DELPHI\LIB\PICEDIT.DCU.中的TPictureEditor。</span></p>
<p><span class="p2"><font size="4"><b>TPropertyEditor</b></font></span></p>
<p><span class="p2">对象察看器为所有的属性提供缺省的编辑。我们可以使用不同的方法重载这种行为,来使用特别的属性编辑器(21种预制的属性编辑器都扩充了对象察看器来处理其属性)。那么,究竟是怎样工作的呢?它是起源一个基类,我们必需重载已达到我们的目的。五个新的Delphi
2.0的方法-其中三个是变量相关的-在编译开关<i>{$IFDEF WIN32}</i>中一保证一下代码在所有的delphi版本中适用。</span></p>
<pre><span class="p2"><b>Type</b>
TPropertyEditor = <b>class</b>
<b>protected</b>
<b>function</b> GetPropInfo: PPropInfo;
<b>function</b> GetFloatValue: Extended;
<b>function</b> GetFloatValueAt(Index: Integer): Extended;
<b>function</b> GetMethodValue: TMethod;
<b>function</b> GetMethodValueAt(Index: Integer): TMethod;
<b>function</b> GetOrdValue: Longint;
<b>function</b> GetOrdValueAt(Index: Integer): Longint;
<b>function</b> GetStrValue: string;
<b>function</b> GetStrValueAt(Index: Integer): string;
<i>{$IFDEF WIN32}</i>
<b>function</b> GetVarValue: variant;
<b>function</b> GetVarValueAt(Index: Integer): variant;
<i>{$ENDIF}</i>
<b>procedure</b> Modified;
<b>procedure</b> SetFloatValue(Value: Extended);
<b>procedure</b> SetMethodValue(const Value: TMethod);
<b>procedure</b> SetOrdValue(Value: Longint);
<b>procedure</b> SetStrValue(const Value: string);
<i>{$IFDEF WIN32}</i>
<b>procedure</b> SetVarValue(const Value: variant);
<i>{$ENDIF}</i>
<b>public</b>
<b>destructor</b> Destroy; override;
<b>procedure</b> Activate; <b>virtual</b>;
<b>function</b> AllEqual: Boolean; <b>virtual</b>;
<b>procedure</b> Edit; <b>virtual</b>;
<b>function</b> GetAttributes: TPropertyAttributes; <b>virtual</b>;
<b>function</b> GetComponent(Index: Integer): TComponent;
<b>function</b> GetEditLimit: Integer; <b>virtual</b>;
<b>function</b> GetName: string; <b>virtual</b>;
<b>procedure</b> GetProperties(Proc: TGetPropEditProc); <b>virtual</b>;
<b>function</b> GetPropType: PTypeInfo;
<b>function</b> GetValue: string; <b>virtual</b>;
<b>procedure</b> GetValues(Proc: TGetStrProc); <b>virtual</b>;
<b>procedure</b> Initialize; <b>virtual</b>;
<i>{$IFDEF WIN32}</i>
<b>procedure</b> Revert;
<i>{$ENDIF}</i>
<b>procedure</b> SetValue(const Value: string); <b>virtual</b>;
<i>{$IFDEF WIN32}</i>
<b>procedure</b> ValueAvailable: Boolean;
<i>{$ENDIF}</i>
<b>property</b> Designer: TFormDesigner read FDesigner;
<b>property</b> PrivateDirectory: string read GetPrivateDirectory;
<b>property</b> PropCount: Integer read FPropCount;
<b>property</b> Value: string read GetValue write SetValue;
<b>end</b>;
</span></pre>
<p><span class="p2">TPropertyEditor编辑对象察看器中一个或是一串控件的一个属性。属性编辑器根据属性的类型而被创建,由RegisterPropertyEditor注册的类型决定。稍候有一个指示程序员如何使用这些工程的例子。所有的published属性都将出现在对象察看器中,当设计者进行读写属性的值时,其属性编辑器(为这种属性类型的)将被使用。</span></p>
<p><span class="p2">在以下的时间里,我们将只注意方法中的需要被重载的重要部分,属性编辑器的行为。</span></p>
<p><span class="p2"><i>GetAttributes</i></span></p>
<p><span class="p2">这是最重要的方法,他决定了属性编辑器的类型和行为。有三种属性编辑器(除了缺省的编辑框):下拉框(我们在前面提到过的),分属性列表和对话框。<br>
GetAttributes返回TPropertyAttributes类型,包含了一下内容: </span><ul>
<li><span class="p2"><i>paValueList:</i>属性编辑器能返回属性的枚举列表。如果GetValues调用过程附带值,这个属性必需设置。这将使在对象察看其中的属性的右边出现下拉按钮。</span></li>
<li><span class="p2"><i>paSubProperties: </i>属性编辑器有子属性时,将在当前属性下方显示成标准的大纲格式。如果GetProperties产生属性对象时这个属性必需设置。</span></li>
<li><span class="p2"><i>paDialog:</i>表示这个编辑方法将产生对话框。这将在对象察看其中的属性右边出现'...'按钮。</span></li>
<li><span class="p2"><i>paSortList:</i> 对象察看器将把GetValues返回的列表按照字母排序。</span></li>
<li><span class="p2"><i>paAutoUpdate:</i> 每当编辑发生改变是调用SetValue方法,而不是改变别提交时。例如Caption属性。</span></li>
<li><span class="p2"><i>paMultiSelect:</i>
允许多个控件被选择时显示属性的值。有些属性不适合多选的情况。例如Name属性。</span></li>
<li><span class="p2"><i>paReadOnly:</i> 属性值不允许改变。</span></li>
<li><span class="p2"><i>GetValue:</i>返回属性的串值,缺省时返回'(unknown)',这应该被重载以返回适当的值。</span></li>
<li><span class="p2">GetValues:当GetAttributes返回paValueList时被调用。它必须为每一个属性所接受的值调用参数函数。TEnumProperty将在列举中传递所有的参数。</span></li>
<li><span class="p2">SetValue(Value):设置属性的值。属性编辑器必须能够知道调用哪一个SetXxxValue函数。如果字符串不是合适的格式或不是合法的值,属性编辑器应该产生一个例外,描述产生的问题。SetValue可以忽略所有的改变,允许通过Edit方法编辑所有的属性。例如Picture属性。</span></li>
<li><span class="p2">Edit<br>
当'...'按钮被安下或是属性被连击识别调用。这样,例如弹出一个对话框,通过更有效的方法,而不是简单的文本来编辑属性。例如Font属性。<br>
</span></li>
</ul>
<p><span class="p2"><font size="4"><b>TFileNameProperty</b></font></span></p>
<p><span class="p2">使用这几个重要的方法我们就能写出自己的属性编辑器了:为filename建立一个大卡文件对话框属性编辑器。我们得记住编写控件从本质来说是非可视化的任务,写书信编辑器并不复杂。我们需要制定一个我们说想要的'Dialog'类型,所以我们在GetAttributes中返回[paDialog]。然后,我们在Edit过程中处理,这次包含一个TOpenDialog来找到任何存在的文件。</span></p>
<pre><span class="p2"><b>unit</b> FileName;
<b>interface</b>
<b>uses</b>
SysUtils, DsgnIntf;
<b>Type</b>
TFileNameProperty = <b>class</b>(TStringProperty)
<b>public</b>
<b>function</b> GetAttributes: TPropertyAttributes; <b>override</b>;
<b>procedure</b> Edit; <b>override</b>;
<b>end</b>;
<b>procedure</b> Register;
<b>implementation</b>
<b>uses</b>
Dialogs, Forms;
<b>function</b> TFileNameProperty.GetAttributes: TPropertyAttributes;
<b>begin</b>
Result := [paDialog]
<b>end</b> <i>{GetAttributes}</i>;
<b>procedure</b> TFileNameProperty.Edit;
<b>begin</b>
<b>with</b> TOpenDialog.Create(Application) <b>do</b>
<b>try</b>
Title := GetName; <i>{ name of property as OpenDialog caption }</i>
Filename := GetValue;
Filter := 'All Files (*.*)|*.*';
HelpContext := 0;
Options := Options + [ofShowHelp, ofPathMustExist, ofFileMustExist];
<b>if</b> Execute <b>then</b> SetValue(Filename);
<b>finally</b>
Free
<b>end</b>
<b>end</b> <i>{Edit}</i>;
<b>procedure</b> Register;
<b>begin</b>
RegisterPropertyEditor(TypeInfo(TFileName),nil, '', TFileNameProperty)
<b>end</b>;
<b>end</b>.
</span></pre>
<p><span class="p2">注意到我们调用属性编辑器的GetName函数来得到属性的名字。</span></p>
<p><span class="p2">属性编辑器需要注册过程(register)在delphi中来注册它本身(确切的说是在delphi应用程序中)。我们可以只为一个控件注册属性编辑器,也可以我所有的相同类型的属性注册。例如上面的例子TFileNameProperty就是为所有的控件做的。当然,属性编辑器必需安装了并且首先注册。</span></p>
<p><span class="p2">为了在Register过程中注册,我们需要调用RegisterPropertyEditor。它有4个参数:第一个是属性类型的类型信息的指针。这里,我们使用内置的函数TypeInfo。第二个是这个编辑器应用的控件类型,如果为nil,这个编辑器为所有控件的所有给定的类型的属性。这里,我们希望属性编辑器为所有的控件的TFileName类型工作。所以只需要把第二个参数置为nil。第三个参数时属性的名字,这个参数只有在第二个参数指定了控件的类型的情况下才有作用。同样,我们把它置为空字符串。第四个参数属性编辑器的自己的类型,这里是TFileNameProperty。</span></p>
<p><span class="p2">安装属性编辑器和安装控件类似。这里,属性编辑器有自己的注册过程(不失为某个控件的属性编辑器,而是某个属性的)。一般来说,如果一个属性编辑器是为特别控件的特别属性,最好和控件一起注册。现在,我们只要把带有TFileNameProperty的单元FILENAME加到控件版中(delphi
1:使用<i>Options | Install Components,</i>Delphi 2中使用<i>Component | Install</i>。</span></p>
<p><span class="p2">安装之后,在任何控件的TFileName类型的属性,我们可以看到省略号,这表明对话框的属性编辑器已在这个属性中安装了。</span></p>
<p align="center"> </p>
<p><span class="p2">如果点击省略号,导致Delphi 2弹出如下对话框。 </span></p>
<p align="center"> </p>
<p><span class="p2">只用了几行代码,我们就写出了为所有控件的TFileName类型的属性的TFileName属性编辑器。这仅仅是个例子,展示了属性编辑器在编写Delphi控件和程序的巨大潜能。</span></p>
<p><span class="p2">在我们研究下一个例子之前,来看看TPropertyEditor其他可以别重载的方法:
</span><ul>
<li><span class="p2"><i>Activate<br>
</i>这个方法在属性被选中时别调用。这可能有用,决定某些属性被选中时的行为。只有GetAttributes返回paSubProperties和paMultiSelect时,才学要准确的控制。</span></li>
<li><span class="p2">AllEqual<br>
当超过一个控件被选中时别调用。如果这个方法返回true,调用GetValue,否则在对象察看其中显示空白。只有在GetAttributes返回paMultiSelect时被调用。</span></li>
<li><span class="p2">GegComponent<br>
返回属性编辑器的控件的索引。当项获得控件时要用倒它。只有在GetAttributes返回paMultiSelect时,属性编辑器才能处理多个控件。</span></li>
<li><span class="p2">GetEditLimit<br>
返回使用这可以输入的值得字符串的个数,对象察看其内置的编辑器对这有限制,缺省值为255。</span></li>
<li><span class="p2">GetName<br>
返回属性的名字,缺省时值时从类型信息中得到的。如果属性的值和对象察看其中所显示的不一样时才有必要重载。</span></li>
<li><span class="p2">GetProperties<br>
应该在被编辑的属性的每一个子属性时重载,调用PropertyProc,并为每一个子属性传递一个新的TPropertyEditor。缺省时,假定没有子属性,PropertyProc不别调用。TClassProperty将为每一个published属性传递一个新的属性编辑器。TSEtProperty为每一个元素传递一个新的编辑器。</span></li>
<li><span class="p2">GetPropType<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -