Delphi 元件設計初步(二)

http://sun.cis.scu.edu.tw/~nms9115/articles/delphi/VclWrite2/VclWrite2.htm

 

Delphi 元件設計初步(二)

作者:曾培彥
日期:July-20-2004

前言

這篇文章, 是本人於點空間看見 Danny Tzu 先生的『Delphi元件設計初步(一)』文章後, 興起寫作的意念. 主要是延續『Delphi元件設計初步.(一)』一文, 讓對於寫Delphi元件有興趣人參考, 本篇文章也延續 Step By Step 概念讓您做出自己的元件.

這篇文章的主題在於『屬性編輯器(Property Editor)』:屬性編輯器可能是整個IDE介面中最常被使用的一種功能之一, 當我們開啟Delphi, 並開始使用物件編輯器(Object Inspector)時, 屬性編輯器可能已經悄悄的被使用了, 屬性編輯器控制著哪些輔助畫面該出現在哪些物件編輯器中的屬性中(Property), 例如當您點選TForm中的TFont屬性時, 您會看見TFont屬性右邊有一個『...』的按鈕,  當您點選『...』時會出現字型編輯畫面, 這就是屬性編輯器(Property Editor)。

更簡單的說, 其實Object Inspector只提供兩種修改屬性的方式, 除了直接讓您輸入文字的屬性外都是屬於屬性編輯器,只是屬性編輯器的種類非常多, 值得注意的是有些屬性只會提供一種方式, 不過也有些屬性提供兩種輸入方式。

在知道什麼是屬性編輯器(Property Editor)後, 我們就開始來研究屬性編輯器(Property Editor)這怎樣實做出來的, 並且知道有哪些屬性編輯器(Property Editor). 另外必須注意的是在VCL中, 早已提供了不少屬性編輯器(Ex. Font, Color, Align...等 ), 當您需要時可以先查詢看看是否有您需要的, 如果沒有再另行製作。

使用環境

Delphi 5, 6 All Version
建議使用 Enterprise 版本可以有很多 VCL Source code 可以參考.

開始吧

撰寫屬性編輯器(Property Editor)大致可區分為以下兩大步驟:

    A. 建立屬性編輯器類別.

    B. 註冊屬性編輯器.

2-1. 屬性編輯器類別

所有的屬性編輯器(Property Editor)必須繼承自TPropertyEditor class, 並且VCL 中找以定義了許多類型的屬性編輯器(Property Editor)(都於DsgnIntf.pas 中), 以下列出較長用的屬性編輯器:

TFontProperty字型屬性編輯器:可以開啟字型設定對話盒, 供使用方便輸入字型相關設定(如字型, 字型樣式, 大小, 效果...等)。
TFontNameProperty預設的字型名稱(TFont.FontName)屬性編輯器。
TOrdinalProperty所有ordinal屬性編輯器的基礎類別。
TIntegerProperty預設所有數字型態的屬性編輯器。
TCharProperty預設所有字元型態的屬性編輯器。如'A'..'Z'。
TEnumProperty預設所有列舉型態的屬性編輯器。如TShape =(sCircle, sTriangle, sSquare)。
TBoolProperty預設所有布林型態的屬性編輯器。
TInt64Property預設所有Int64數字型態的屬性編輯器。
TFloatProperty預設所有浮點數(Float, Single, Double)型態的屬性編輯器。
TStringProperty預設所有字串型態的屬性編輯器。
TNestedProperty 
TSetElementProperty預設所有集合型態元素的屬性編輯器。
TSetProperty預設所有集合型態的屬性編輯器。
TClassProperty預設的屬性編輯器。
TMethodProperty預設的方法(事件)屬性編輯器。
TComponentProperty預設的元件屬性編輯器。
TComponentNameProperty預設的元件名稱屬性編輯器。

TPropertyEditor 中重要的Method。


TPropertyEditor = class
private
...
function GetPrivateDirectory: string;
procedure SetPropEntry(Index: Integer; AInstance: TPersistent;APropInfo: PPropInfo);
protected
...
function GetOrdValue: Longint;
    //傳回目前的屬性(Property)值. 
procedure SetOrdValue(Value: Longint);
    //設定目前的屬性(Property)值. 
  ...
public
...
function AllEqual: Boolean; virtual;
    //表示一個以上知被選擇的元件是否具有相同的值. 當有一個以上的元件被選擇時, 此方法被呼叫.
procedure Edit; virtual;
    //當使用者對'...'按鈕雙擊(double-clicked)時被呼叫.
    //大都用於顯示視窗盒, Ex.Font property.
function GetAttributes: TPropertyAttributes; virtual;
    //傳回在Object Inspector 中顯示的形式, 包含以下.
    //  paValueList:           下拉式選單, 類似ComboBox 形式.
paSortList: 物件顯示器經由 GetValues 排序屬性(Property).
paSubProperties: 具有可展開式(子屬性)屬性編輯器. 必須配合 GetProperties 方法.
paDialog: 具有視窗盒的屬性編輯器. 以'...'按鈕的形式顯示於物件顯示器中.
paMultiSelect: 具有可選取多個元件同時異動屬性值的功能. 當選擇多個元件時,
依然出現於物件顯示器中. 某些屬性(Property)是不具多選擇功能的
                         (如. Name 屬性).
paAutoUpdate: 每當屬性(Property)有異動時即呼叫 SetValue 方法, 如果沒有設定,
SetValue 方法只有在游標移開或按下 Enter 時被呼叫.
paReadOnly: 唯讀的屬性(Property)功能.
paRevertable: 具有'ESC'鍵的屬性(Property)功能. 有些屬性不能設定此種功能(如. Font)
paFullWidthName: 具有顯示全部屬性值的功能. 這樣的功能會自動調整物件顯示器的寬度.
function GetComponent(Index: Integer): TPersistent;
    //表示目前正在編輯的屬性(Property).
function GetEditLimit: Integer; virtual;
    //於字串屬性(Property), 表示屬性(Property)之最大字元長度.
procedure GetProperties(Proc: TGetPropEditProc); virtual;
    //當子屬性(Sub-Propertys)顯示時此方法被呼叫. 在此時做適當的子屬性(Sub-Propertys).
function GetValue: string; virtual;
//傳入此屬性編輯器在Object Inspector中顯示的文字字串.
  procedure GetValues(Proc: TGetStrProc); virtual;
     //傳入此屬性編輯器在Object Inspector中顯示的文字字串列舉值 , 此方法於使用者點選下拉選單時呼叫.
procedure SetValue(const Value: string); virtual;
    //接收使用者於Object Inspector中輸入的字串.
...
end;

2-2.  建立屬性編輯器類別

在瞭解了屬性編輯器類別後, 接下來的就是建立一個屬於自己需要的屬性編輯器類別.

首先, 假設我們要建立一個表示星期幾的屬性編輯器類別 TWeekNameProperty, 繼承自 TIntegerProperty, 並且定義好表示星期幾的字串陣列變數(如範例中的程式), 然後改寫 GetValue 及 SetValue 這兩個方法. 

另外於實際運用時還需要一個儲存星期幾的屬性(Property), 這個需求使用一個整數型態儲存, 然而在物件顯示器中卻要以字串表示, 所以我們必須自訂一個整數型態 (TWeekName, 如範例中的程式) 名稱.

 GetValue 方法的作用是由事先已經定意義好的字串陣列中取得值顯示於物件顯示器. 

另 SetValue 方法是傳回使用者由物件顯示器中輸入的值, 在 SetValue 方法中, 必須針對由物件顯示器傳來的值做嚴格的檢查, 因此屬性編輯器類別可以接受兩種資料型態, 一種是數字, 即為一個代表星期幾字串陣列中的指標數值 , 另一個為星期幾的字串名稱, 所以在此方法中必需做以下的檢查:

    a. 當輸入值為名稱時, 檢查是否符合星期幾的名稱, 如'Sunday', 'Monday'...等. 

    b. 當輸入值是一個指標(數字)時, 檢查指標標是否符合合理的範圍(1..7).

    Note : 針對以上檢查, 於 a 中, 也可提供具有可輸入縮寫的功能, 如'Sun', 'Mon'...等, 

              這樣的功能可以在 SetValue 方法中實做, 在此篇文章中我們不詳述.

實做屬性編輯器 TWeekNameProperty 


unit Week;

interface

uses
Windows, SysUtils, DsgnIntF, classes;

type
TWeekName = type Integer;
TWeekNameProperty = class(TIntegerProperty)
public
function GetValue: string; override;
procedure SetValue(const Value: string); override;
end;

implementation

const
//定義星期之陣列
WeekNames: array[1..7] of String[9] =
('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
'Saturday', 'Sunday');


function TWeekNameProperty.GetValue: string;
begin
Result := WeekNames[GetOrdValue];
end;

procedure TWeekNameProperty.SetValue(const Value: String);
var
sWName: string[9];
iIndex, ValErr: Integer;
begin
sWName := UpperCase(Value);
iIndex := 1;
//取得 Value 於陣列中的位置.
while (sWName <> UpperCase(WeekNames[iIndex])) and (iIndex < 10) do
inc(iIndex);

//判斷是否屬合理的值.
if (iIndex < 8) and (iIndex > 0) then
begin
SetOrdValue(iIndex);
Exit;
end
else
begin
//可能輸入 Week Name, 或無效的值.
Val(Value, iIndex, ValErr);
if ValErr <> 0 then
raise Exception.Create(Format('輸入的星期不符合 %s.',[Value]));
if (iIndex > 7) or (iIndex=0) then
raise Exception.Create(Format('輸入的代碼不符合 %s.',[Value]));
SetOrdValue(iIndex);
end;
end;
end.
 

2-3.  註冊屬性編輯器.

註冊屬性編輯器(Property Editor)是一件容易的事, 我們只需要透過呼叫  RegisterPropertyEditor 方法即可, 該方法的宣告如下:

procedure RegisterPropertyEditor(PropertyType: PTypeInfo; ComponentClass: TClass; const PropertyName: string; EditorClass: TPropertyEditorClass);

  PropertyType     指定屬性型態的執行時期型別資訊結構指標. 可經由呼叫 TypeInof 函式取得一函式的指標.

  ComponentClass  指定屬性編輯器會作用的元件類別. 如果要所有元件的都作用, 可指定 nil.

  PropertyName    指定屬性編輯器會作用的屬性名稱. 如果要所有屬性的都作用, 可指定空字串.

  EditorClass        指定屬性編輯器的類別.

 

註冊屬性編輯器


RegisterPropertyEditor(Typeinfo(TWeekName),TCourse,'StudyDay',TWeekNameProperty);
//註冊 TWeekNameProperty 屬性編輯器, 於 TCourse 元件中的 TWeekName 型態之  StudyDay 屬性使用.
 
//如果要於所有元件中任何屬性名稱都可使用 TWeekNameProperty, 可使用以下的註冊方式.
//RegisterPropertyEditor(Typeinfo(TWeekName), nil,'',TWeekNameProperty);

2-4. 使用屬性編輯器 

在經由前述之步驟(2-3, 2-4)之後, 我們已完成了屬性編輯器的製作. 

接下來就是如何使用已經製作好的屬性編輯器? 設定哪個屬性具有這樣的屬性編輯器功能, 只需將該屬性設定為該屬性型態(Property Type)即可. 以下就以一個 TCourse 元件作為示範, 結果顯示於圖一.


TCourse = class(TComponent) //課程
private
FStudyDay : TWeekName;
public
constructor Create(AOwner: TComponent); override;
published
property StudyDay: TWeekName read FStudyDay write FStudyDay;
end;

implementation
constructor TCourse.Create(AOwner: TComponent);
begin
inherited;
FStudyDay := 1;
end;

2-5.  建立下拉式選單之屬性編輯器

下拉式選單之之屬性編輯器, 只需定義一個列舉型態即可. 

type
  TTimeClass = (tcMorning, tcAfternoon ); //早課, 下午課

TCourse = class(TComponent) //課程
private
  ...
  FTimeClass : TTimeClass;
public
  ...
published
  ...
  property TimeClass : TTimeClass read FTimeClass write FTimeClass;
end;

 

待續...

  •  元件編輯器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值