荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Peter (小飞侠), 信区: Program
标  题: Delphi自定义部件(三)
发信站: BBS 荔园晨风站 (Fri Jan 22 13:46:04 1999), 转信



19.2.2 Delphi部件编程

19.2.2.1 创建属性

   属性(Property)是部件中最特殊的部分,主要因为部件用户在设计时可以看见和
操作它们,并且在交互过程中能立即得到返回结果。属性也很重要,因为如果将它们设
计好后,将使用户更容易地使用,自己维护起来也很容易。
  为了使你在部件中更好地使用属性,本部分将介绍下列内容:
   ● 为什么要创建属性
   ● 属性的种类
   ● 公布(publishing)继承的属性
   ● 定义部件属性
   ● 编写属性编辑器

  1. 为什么要创建属性
  属性提供非常重要的好处,最明显的好处是属性在设计时能出现在Object Inspecto
r窗口中,这将简化编程工作,因为你只需读用户所赋的值,而不要处理构造对象的参数。
  从部件使用者的观点看,属性象变量。用户可以给属性赋值或读值,就好象属性是
对象的域。
  从部件编写者的观点看属性比对象的域有更强的功能;
  ⑴ 用户可以在设计时设置属性
  这是非常重要的,因为不象方法,只能在运行时访问。属性使用户在运行程序之前
就能定制部件,通常你的部件不应包含很多的方法,它们的功能可以通过属性来实现。
  ⑵ 属性能隐藏详细的实现细节
   ⑶ 属性能引起简单地赋值之外的响应,如触发事件
  ⑷ 用于属性的实现方法可以是虚拟方法,这样看似简单的属性在不同的部件中,将
实现不同的功能。

   2. 属性的类型
  属性可以是函数能返回的任何类型,因为属性的实现可以使用函数。所有的Pascal
类型,兼容性规则都适用属性。为属性选择类型的最重要的方面是不同的类型出现在Obje
ct Inspector窗口中的方式不同。Object Inspector将按不同的类型决定其出现的方式。
    你也能在注册部件时描述不同的属性编辑器。
  下表列出属性出现在Object Inspector窗口中的方式

                    表19.3  属性出现在Object Inspector窗口中的方式
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  属性类型       处   理   方   式
───────────────────────────────────────
  简单类型  Numeric、Character和 String属性出现在Object Inspector中,用户可
               以直接编辑
  枚举类型     枚举类型的属性显示值的方式定义在代码中。选择时将出现下拉

               式列表框,显示所有的可能取值。
  集合类型     集合类型出现在Object Inspector窗口中时正如一个集合,展开后,用

               户通过将集合元素设为True或False来选择。
  对象类型     作为对象的属性本身有属性编辑器,如果对象有自己的published属
               性,用户在Object Inspector中通过展开对象属性列,可以独立编辑它们

               对象类型的属性必须从TPersistent继承。
  数组类型     数组属性必须有它们自己的属性编辑器,Object Inspector没有内嵌对数
               组属性编辑的支持。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

    3. 公布继承的属性
  所有部件都从祖先类型继承属性。当你从已有部件继承时,新部件将继承祖先类型
的所有属性。如果你继承的是抽象类,则继承的属性是protected或public,但不是publi
shed。如想使用户访问protected或public属性,可以将该属性重定义为published。如
果你使用TWinControl继承,它继承了Ctl3D属性,但是protected的,因此用户在设计和
运行时不能访问Ctl3D,通过在新部件中将Ctl3D重声明为published,就改变了Ctl3D的
访问级别。下面的代码演示如何将Ctl3D声明为published,使之在设计时可被访问。

  type
      TSampleComponent=class(TWinControl)
      published
        property Ctl3D;
      end;

    4. 定义部件属性
  ⑴ 属性的声明
  声明部件的属性,你要描述:
   ● 属性名
   ● 属性的类型
  ● 读和设置属性值的方法

  至少,部件属性应当定义在部件对象声明的public部分,这样可以在运行时很方便
地从外部访问;为了能在设计时编辑属性,应当将属性在published部分声明,这样属性
能自动显示在Object Inspector窗口中。下面是典型的属性声明:

  type
      TYourComponent=class(TComponent)
           …
      private
        FCount: Integer   { 内部存储域 }
        function GetCount: Integer;    { 读方法 }
        procedure SetCount(ACount: Integer);    {  写方法 }
      pubilic
        property Count: Integer read GetCount write SetCount;
      end;

  ⑵ 内部数据存储
  关于如何存储属性的数据值,Delphi没有特别的规定,通常Delphi部件遵循下列规
定:
   ● 属性数据存储在对象的数据域处
   ● 属性对象域的标识符以F开头,例如定义在TControl中的属性FWidth
  ● 属性数据的对象域应声明在private部分

  后代部件只应使用继承的属性自身,而不能直接访问内部的数据存储。
  ⑶ 直接访问
  使属性数据可用的最简单的办法是直接访问。属性声明的read 和write部分描述了
怎样不通过调用访问方法来给内部数据域赋值。但一般都用read进行直接访问,而用writ
e进行方法访问,以改变部件的状态。
  下面的部件声明演示了怎样在属性定义的read 和write部分都采用直接访问:

  type
      TYourComponent=class(TComponent)
               …
      private    {  内部存储是私有  }
        FReadOnly: Boolean;   {  声明保存属性值的域  }
      published      {  使属性在设计时可用  }
        property ReadOnly: Boolean read FReadOnly write FReadOnly;
      end;

  ⑷ 访问方法
  属性的声明语法允许属性声明的read和write部分用访问方法取代对象私有数据域。
不管属性是如何实现它的read 和write部分,方法实现应当是private,后代部件只能使
用继承的属性访问。
  ① 读方法
    属性的读方法是不带参数的函数,并且返回同属性相同类型的值。通常读函数的名
字是“Get”后加属性名,例如,属性Count的读方法是GetCount。不带参数的唯一例外
是数组属性。如果你不定义read方法,则属性是只写的。
  ② 写方法
  属性的写方法总是只带一个参数的过程。参数可以是引用或值。通常过程名是"Set"
加属性名。例如,属性Count的写方法名是SetCount。参数的值采用设置属性的新值,因
此,写方法需要执行在内部存储数据中写的操作。
  如果没有声明写方法,那么属性是只读的。
  通常在设置新值前要检测新值是否与当前值不同。
  下面是一个简单的整数属性Count的写方法:

  procedure TMyComponent.SetCount( value: Integer);
    begin
      if value <>FCount then
      begin
        FCount := Value;
        update;
      end;
    end;

    ⑸ 缺省属性值
  当声明一个属性,能有选择地声明属性的缺省值。部件属性的缺省值是部件构造方
法中的属性值集。例如,当从Component Palette选择某部件置于窗体中时,Delphi通过
调用部件构造方法创建部件,并决定部件属性初始值。
  Delphi使用声明缺省值决定是否将属性值存在DFM文件中。如果不描述缺省值,Delph
i将总是保存该属性值。声明缺省值的方法是在属性声明后加default指令,再跟缺省值。
  当重声明一个属性时,能够描述没有缺省值的属性。如果继承的属性已有一个,则
设立没有缺省值的属性的方法是在属性声明后加nodefault指令。如果是第一次声明属
性,则没有必要加nodefault指令,因为没有default指令即表示如此。
  下例是名为IsTrue的布尔类型属性设置缺省值True的过程:

  type
      TSampleComponent=class(TComponent)
      private
        FIsaTrue: Boolean;
      pubilic
        constructor Create (AOwner: TComponent); Overvide;
      published
        property Istrue: Boolean read FIsTrue write FIsTrue default True;
      end;

      constructor TSampleComponent.Create (AOwner: TComponent);
      begin
        inherited Create ( Aowner);
        Fistvue := True;   {  设置缺省值  }
      end;

     5. 编写属性编辑器
    Object Inspector提供所有类型属性的缺省编辑器,Delphi也支持通过编写和注册
属性编辑器的方法为属性设计自己的编辑器。可以注册专门为自定义部件的属性设计的
编辑器,也可设计用于所有某类型的属性。编写属性编辑器需要下列五个步骤:
    ● 继承一个属性编辑器对象
   ● 将属性作为文本编辑
   ● 将属性作为整体编辑
  ● 描述编辑器属性
   ● 注册属性编辑器

  ⑴ 继承属性编辑器对象
  DsgnIntf库单元中定义了几种属性编辑器。它们都是从TPropertyEditor继承而来。
当创建属性编辑器时,可以直接从TPropertyEditor中继承或从表中的任一属性编辑器中
继承。


                         表19.4  属性编辑器的类型
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      类型              编辑的属性
   ─────────────────────────────────────
     TOrdinalProperty   所有有序的属性(整数、字符、枚举)
     TIntegerProperty   所有整型,包括子界类型



     TCharProperty    字符类型或字符子集
     TEnumProperty    任何枚举类型
     TFloatProperty     所有浮点数
     TStringProperty    字符串,包括定长的字符串
     TSetElementProperty  集合中的独立元素
     TSetElementProperty  所有的集合,并不是直接编辑集合类型,而是展开成一列
                          集合元素属性
     TClassProperty       对象,显示对象名,并允许对象属性的展开
     TMethodPropevty     方法指针,主要指事件
     TComponentProperty  相同窗体中的部件,用户不能编辑部件的属性,
                          但能指向兼容的部件
     TColorProperty    部件颜色,显示颜色常量,否则显示十六进制数
     TFontNameProperty   字体名称
     TFontProperty        字体,允许展开字体的属性或弹出字体对话框
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

    下面是TFloatPropertyEditor的定义:

    type
      TFloatProperty=Class(TPropertyEditor)
      public
        function AllEqual: Boolean; override;
        function GetValue: String; override;
        procedure SetValue ( Const Value: string ); override;
      end;

    ⑵ 象文本一样编辑属性
  所有的属性都需要将它们的值在Object Inspector窗口中以文本的方式显示。属性
编辑器对象提供了文本表现和实际值之间转换的虚方法。这些虚方法是GetValue和SetVal
ue,你的属性编辑器也能继承了一系列的方法用于读和写不同类型的值。见下表:

                       表19.5  读写属性值的方法
           ━━━━━━━━━━━━━━━━━━━━━━━━━━
               属性类型    "Get"方法         "Set"方法
           ──────────────────────────
               浮点数      GetFloatValue         SetFloatVallue
               方法指针    GetMethodValue       SetMehodValue
               有序类型    GetOrdValue          SetOrdValue
               字符串      GetStrValue           SetStrValue
           ━━━━━━━━━━━━━━━━━━━━━━━━━━

    当覆盖GetValue方法时,调用一个"Get"方法;当覆盖SetValue方法时调用一个"Set
"方法。
  属性编辑器的GetValue方法返回一个字符串以表现当前属性值。缺省情况下GetValu
e返回"unknown"。
    属性编辑器的SetValue接收Object Inspector窗口String类型的参数,并将其转换
成合适的类型,并设置属性值。
  下面是TIntegerProperty的GetValue和SetValue的例子:

    function TIntegerProperty GetValue: string;
    begin
      Result := IntToStr (GetOrdValue);
    end;

    proceduve TIntegerPropertySetValue (Const Value: string);
    var
      L: Longint;
    begin
      L := StrToInt(Value);  { 将字符串转换为数学 }
      with GetTypeData (GetPropType)^  do
        if  ( L < Minvalue ) or ( L > MaxValue ) then
          Raise EPropertyError.Create (FmtloadStr(SOutOfRange,
                                   [MinValue,MaxValue]));
      SetOrdValue (L);
    end;

    ⑶ 将属性作为一个整体来编辑
  Delphi支持提供用户以对话框的方式可视化地编辑属性。这种情况常用于对对象类
型属性的编辑。一个典型的例子是Font属性,用户可以找开Font对话框来选择字体的属
性。
  提供整体属性编辑对话框,要覆盖属性编辑对象的Edit方法。Edit方法也使用"Get"
和"Set"方法。
   在大多数部件中使用的Color属性将标准的Windows颜色对话框作为属性编辑器。下
面是TColorProperty的Edit方法

    procedure TColorProperty.Edit
    var
      ColorDialog: TColorDialog;
    begin
      ColorDialog := TColorDialog.Create(Application);  { 创建编辑器 }
      try
       ColorDialog.Color := GetOrdValue;  { 使用已有的值 }
       if ColorDialog.Execute then
       SetOrdValue (ColorDialog.Color);
      finally
        ColorDialog.Free;
      end;
   end;

    ⑷ 描述编辑器的属性
  属性编辑必须告诉Object Inspector窗口如何采用合适的显示工具。例如Object Ins
pector窗口需要知道属性是否有子属性,或者是否能显示可能取值的列表。描述编辑器
的属性通常覆盖属性编辑器的GetAttributes方法。
    GetAttributes返回TPropertyAttributes类型的集合。集合中包括表中任何或所有
的值:

                       表19.6 属性编辑器特征标志
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━
             标志         含   义                  相关方法
        ───────────────────────────
             paValuelist     编辑器能给予一组枚举值    GetValues
             paSubPropertie  属性有子属性             GetPropertises
             paDialog       编辑器能显示编辑对话框    Edit
             PaMultiSelect   当用户选择多于一个部件
                             时,属性应能显示         N/A
         ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    Color属性是灵活的,它允许在Object Inspector窗口中以多种方式选择他们。或者
键入,或者从列表中选择定编辑器。因此TColorProperty的GetAttributes方法在返回值
中包含多种属性。

  function TColorProperty.GetAttributes: TProrertyAttributes;
     begin
       Result := [PaMultiselect, paDialog, paValuelist];
     end;

   ⑸ 注册属性编辑器
  一旦创建属性编辑器,必须在Delphi中注册。注册属性编辑器时,要与某种属性相
联。
  调用RegisterPropertyEditor过程来注册属性编辑器。该过程接受四个参数:
  ● 要编辑的属性的类型信息的指针。这总是通过调用调用TypeInfo函数得到,如Typ
eInfo ( TMyComponent )
  ● 编辑器应用的部件类型,如果该参数为nil则编辑器应用于所给的类型的所有属性
  ● 属性名,该参数只有在前一参数描述了部件的情况下才可用
  ● 使用该属性编辑器的属性的类型

  下面引用了注册标准部件的过程:

    procedure Register;
    begin
      RegisterPropertyEditor (TypeInfo(TComponent), nil, TComponentProperty,
      RegisterPropertyEditor(TypeInfo(TComponentName), TComponent,
                         'Name', (ComponentNamePropety);
      RegisterPropertyEditor (TypeInfo(TMenuItem), TMenu, '', TMenuItemProperty)
;
    end;

  这三句表达式使用RegisterPropertyEditor三种不同的用法:
  ● 第一种最典型
    它注册了用于所有TComponent类型属性的属性编辑器TComponentProperty。通常,
当为某种类型属性注册属性编辑器时,它就能应用于所有这种类型的属性,因此,第二
和第三个参数为nil。
  ● 第二个表达式注册特定类型的属性编辑器
    它为特定部件的特定属性注册属性编辑器,在这种情况下,编辑器用于所有部件的Na
me属性。
   ● 第三个表达式介于第一个和第二个表达式之间
    它为部件TMenu的TMenuItem类型的所有属性注册了属性编辑器。

--
※ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.1.3]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店