属性分类及其实现
Posted jijm123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了属性分类及其实现相关的知识,希望对你有一定的参考价值。
在Delphi 5 中,它的Object Inspector又有了新的功能上的增强,这就是属性分类。
属性分类
看下面的图3.1,这会注意到在状态栏中的这句话:"2 hidden."这表明我们现在可以从Object Inspector的视图中隐藏属性。这就是将要谈到的属性分类的一部分特性。
Delphi 5提供了类似Visual Basic的对事件和属性进行分组的能力。通常条件下,缺省设置的Object Inspector视图按字母排序所有的属性(包括很多很少使用的属性和事件),这样的话,在很多属性中定位需要编辑的属性就比较麻烦。而现在,我们可以通过定制Object Inspector来按分类来显示属性。如下图3.2所示:
当我们按分类显示属性后,注意我们仍然可以看到状态条上的"2 hidden"信息。调出右键菜单,我们会注意到"Legacy"类别缺省时是隐藏的。这个类别包含了Ctl3D和OldCreateOrder属性,是为了兼容性才保留,对于新的程序已经没有意义了,所以缺省时会被隐藏起来
分类显示属性可以更好的说明属性的用途和意义。特别是当你仅仅想设定显示属性的时候,可以不受那些不相关的属性的干扰。当然需要知道的是一个属性可以分别属于多个类别。如下图3.4所示:
就象我们看到的Hint属性分别属于"Actions"和"Help and Hints"类别,除此之外还有很多属性,比如Caption可以同时属于多个类别。
Object Inspector会在状态条显示当前有多少隐藏属性,然而一些被隐藏的属性可能会在其他类别中显示出来(所以隐藏的属性数可能会比实际唯一被隐藏的属性数值大)。
定制属性分类
那么如何指定我们自己需要的属性分类呢?比如,我们可能想在自己设计的一个控件中建立一个新的属性分类。这时,我们就需要注册新的属性分类。注意我们不能修改已经分好类的属性。
下面的函数就是Delphi 5 (声明在dsgnintf.pas单元中)中用来定制属性分类的:
type
TPropertyCategoryClass = class of TPropertyCategory;
RegisterPropertyInCategory(ACategoryClass: TPropertyCategoryClass;
const APropertyName: String): TPropertyFilter; overload;
RegisterPropertyInCategory(ACategoryClass: TPropertyCategoryClass;
const APropertyName: String;AComponentClass: TClass): TPropertyFilter; overload;
RegisterPropertyInCategory(ACategoryClass: TPropertyCategoryClass;
const APropertyName: String;APropertyType: PTypeInfo): TPropertyFilter; overload;
RegisterPropertyInCategory(ACategoryClass: TPropertyCategoryClass;
APropertyType: PTypeInfo): TPropertyFilter; overload;
RegisterPropertyInCategory函数有四种调用格式。我们可以以这四种格式指定一个分类过滤器:
(1)属性名
(2)对象类型和属性名
(3)属性类型和属性名
(4)属性类型
属性名支持通配符,比如我们把所有符合“My*”的属性分成一个特殊的分类。同时要想注册一系列的属性名或属性类型过滤器,可以使用RegisterPropertiesInCategory函数, 用CategoryClass和常数数列作为参数。我们可以使用下列预定义的类别对象 (在DESNINTF.PAS中定义的): TActionCategory, TDataCategory, TDatabaseCategory, TDragNDropCategory, THelpCategory, TLayoutCategory, TLegacyCategory, TLinkageCategory, TLocaleCategory, TLocalizableCategory, TMiscellaneousCategory, TVisualCategory, TInputCategory, 或 TMidasCategory (在MIDREG.PAS中定义的)。
添加属性到预定义的类别
例如,要想设定一个新的名为TATButton的控件的About属性为TVisualCategory属性类别,可以使用下列代码:
unit button;
interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs,
StdCtrls, DsgnIntf;
Type
TATButton = class(TButton)
Private
{ Private declarations }
FAbout: String;
Published
{ Published declarations }
property About: String read FAbout write FAbout;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterPropertyInCategory(
TVisualCategory,TATButton,‘About‘);
RegisterComponents(‘Property Category‘, [TATButton]);
end;
end.
下图3.5就是运行结果:
上面我们用了系统预定义的分类对象,接下来的问题是,如果我们不知道要传递的分类对象的类型名,如何传递CategoryClass参数?
通过调用PropertyCategoryList函数可以得到全部TPropertyCategory对象列表。每个TPropertyCategory对象都有Name和Description方法可以用来确定要添加的属性类别。但是又有一个问题,我们无法把TPropertyCategory的实例作为第一个参数传递给RegisterPropertyInCategory函数。我们需要类引用而不是实例。而传递ClassType的值也不能解决问题,这个方法只能返回TClass而不是TPropertyCategory。我们无法使用AS操作符把TClass映射为TPropertyCategory,因为AS只对实例有效。幸好我们可以使用强制类型映射,就象下面代码所示意的那样:
unit button;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, DsgnIntf;
Type
TATButton = class(TButton)
Private
{ Private declarations }
FAbout: String;
Published
{ Published declarations }
property About: String read FAbout write FAbout;
end;
procedure Register;
implementation
procedure Register;
var
PropertyCategories: TPropertyCategoryList;
i: Integer;
begin
PropertyCategories := PropertyCategoryList;
for i:=0 to Pred(PropertyCategories.Count) do
if PropertyCategories.Categories[i].Name = ‘Visual‘ then
RegisterPropertyInCategory(// 强制类型映射 TPropertyCategoryClass(
PropertyCategories.Categories[i].ClassType), TATButton,‘About‘);
RegisterComponents(‘Property Category‘, [TATButton]);
end;
end.
属性过滤器
仔细研究过RegisterPropertyInCategory函数内部机制后,就会发现其实上面的问题还有其他的解决办法。这就是通过动态建立一个新的属性过滤器(TPropertyFilter)并添加这个过滤器到属性类别的实例中去。代码如下:
unit button;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, DsgnIntf;
Type
TATButton = class(TButton)
Private
{ Private declarations }
FAbout: String;
Published
{ Published declarations }
property About: String read FAbout write FAbout;
end;
procedure Register;
implementation
procedure Register;
var
PropertyCategories: TPropertyCategoryList;
i: Integer;
begin
for i:=0 to Pred(PropertyCategories.Count) do
if PropertyCategories.Categories[i].Name =
‘Visual‘ then
PropertyCategories.Categories[i].Add(
TPropertyFilter.Create(‘About‘, TATButton, nil));
RegisterComponents(‘Property Category‘, [TATButton]);
end;
end.
在这里,属性过滤器起到了连接属性和对象类型的作用。
同时注册多个属性
如果想同时注册一组属性的话,可以使用RegisterPropertiesInCategory函数的重载版本,用CategoryClass和一组常数作为参数:
function RegisterPropertiesInCategory(
ACategoryClass: TPropertyCategoryClass;
const AFilters: array of const):
TPropertyCategory; overload;
function RegisterPropertiesInCategory(
ACategoryClass: TPropertyCategoryClass;
AComponentClass: TClass; const AFilters:
array of String): TPropertyCategory; overload;
function RegisterPropertiesInCategory(
ACategoryClass: TPropertyCategoryClass;
APropertyType: PTypeInfo; const AFilters:
array of String): TPropertyCategory; overload;
注意如果想注册一个或多个属性到超过一个的类别中的话,必须按不同类别分别调用RegisterPropertyInCategory或RegisterPropertiesInCategory函数。
属性是否已分类?
要想知道一个对象的属性是否已经分了类别的话,可以调用IsPropertyInCategory函数:
IsPropertyInCategory(CategoryClass,
ComponentClass, PropertyName): Boolean;
IsPropertyInCategory(CategoryClass,
ComponentClassName, PropertyName): Boolean;
用户定制的属性类别
要想定义一个新的属性类别,需要从TPropertyCategory衍生一个新类,并实现两个类方法: Name和Description。注意这里说的是类方法,Object Inspector 需要在没有属性类实例情况下调用Name和Description方法(换句话说,类方法可以使我们不用Create就可以直接调用TPropertyCategory.Name和TPropertyCategory.Description方法)。类方法的实现很简单,只要在方法前加上关键词class就行了。
下面的例子实现了一个新的属性类别TDemoPropertyCategory, 它的Name类方法返回"DEMO",Description类方法返回"DEMO Property Category":
unit button;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, DsgnIntf;
Type
TATButton = class(TButton)
Private
{ Private declarations }
FAbout: String;
Published
{ Published declarations }
property About: String read FAbout write FAbout;
end;
procedure Register;
implementation
type
TDemoPropertyCategory = class(TPropertyCategory)
Public
//关键词class表示实现的是类方法
class function Name: string; override;
class function Description: string; override;
end;
class function TDemoPropertyCategory.Description:
string;
begin
Result := ‘Demo Property Category‘;
end;
class function TDemoPropertyCategory.Name: string;
begin
Result := ‘Demo‘;
end;
procedure Register;
begin
RegisterPropertyInCategory(
TDemoPropertyCategory,TATButton,‘About‘);
RegisterComponents(‘Demo‘, [TATButton]);
end;
end.
控件安装后的结果如下图所示:
注意:Name类方法完全可以返回一个已经定义好的名字,比如"Visual",这时你会得到两个同样名字的属性类别。
DSGNINTF和发布
Delphi 5 README.TXT里面提到DSGNINTF.DCU今后将不再随Delphi发布了。因此,控件开发者应该考虑把设计时和运行时代码放在不同的单元中。运行时代码也不能引用这个单元。
我们也可以编译DSGNINTF.PAS后发布。但这只能作为一个权宜之计,因为无法应用到将来的版本。
结论
属性分类是Object Inspector一个很有用增强特性。同时用户定制类别的能力对于更好的管理控件设计也有好处,但要注意的是如果每个人都引入一大堆自定义的类别,可能会引起很大的混乱。因此,一定要谨慎的使用这项技术。
以上是关于属性分类及其实现的主要内容,如果未能解决你的问题,请参考以下文章