自從Delphi6出來以後,一個經常被提到的問題是Proxies.pas檔案從源檔案中消失了。
這個改變是大趨勢的一個部分。Borland在Delphi 5中沒有裝載DsgnIntf.dcu,這顯然是要強迫迎合Delphi和C++Builder的許可協定。運作時代碼在很多控件中常被不經意地用到。在某些方面Borland鼓勵:如果你運用新的控件向導,你将發現這個向導隻建立了一個單元,它把控件運作時的架構代碼和注冊函數放在同一個單元中。
在Delphi6中Borland更進一步,不僅用DesignIntf替換了DsgnIntf,而且屬性編輯器也被放進了DesignEditors,DesignMenus,DesignWindows和其它的一些設計檔案裡。特别是DesignEditors使用了其它的一個名叫Proxies的IDE檔案。(Proxies代碼放在DesignIDE.bpl檔案中。)不用說這些改變将會産生編譯時的錯誤。
如果你的運作時代碼已經與設計時代碼分開了,那麼就很容易修改。打開設計時包,然後選擇一個目錄,點選Add按鈕。填寫designide.dcp和點選确定。重新編譯你的包,這時錯誤已經沒有了。
如果你的設計時代碼和運作時代碼已經混合在一起了,那應該怎麼解決呢?在Delphi裡DesignIDE.bpl不是一個可以再分發的包。是以,即使是隻是設計時包使用了元件的運作時代碼或是隻是控件dcu用了都将産生問題。
99.99%的情況事實上很容易解決。你的運作時代碼事實上沒有使用設計時代碼;問題是沒有合适的分開。
設計時包應該包括:
1、所有的注冊聲明。
2、所有的屬性編輯器。
3、所有的元件編輯器。
4、将需要DesignIDE和每一個儲存元件自己的運作時包。
運作時包應該包括:
1、元件自己。
2、任何編輯器也許會用到的元件可能自己在運作時調用的窗體。
維一有點混惑的地方是:屬或元件是否使用了一個窗體。假如這個窗體在運作時對于元件是可用的,那麼它應該包含在運作時包裡。如果它隻是在設計時可使用,那它就應該包含在設計時包裡。一個常見的錯誤是誤認這個窗體本身是一個編輯器,但事實上它不是。而是元件編輯器調用了這個窗體,它是設計時編輯器。
你應該養成一個把元件分開成兩個包的習慣,即使是你隻在程式中靜态地進行連結,因為混合運作時和設計時代碼将使你的代碼膨脹。你的設計時代碼在運作時不會被執行,但是連結器不會知道,是以把它也一起連結進去了。(這就是為什麼DsgnIntf要設法連結進去的原因。)
讓我們看一個簡單的例子,了解如何把設計時代碼從運作時代碼中分離出去:
{ TMixedComponent }
TMixedComponent = class(TComponent)
private
FFileName: String;
published
property FileName : String read FFileName write FFileName;
{ Published declarations }
end;
{ TMixedFileNameProperty }
TMixedFileNameProperty = class(TPropertyEditor)
function AllEqual: boolean; override;
procedure Edit; override;
function GetAttributes: TPropertyAttributes; override;
function GetValue: string; override;
procedure SetValue (const Value: string); override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(string), TMixedComponent, ’FileName’,
TMixedFileNameProperty);
RegisterComponents(’Samples’, [TMixedComponent]);
end;
function TMixedFileNameProperty.AllEqual: boolean;
var
FirstVal: string;
i: Integer;
begin
FirstVal := GetStrValue;
Result := True;
i := 1;
while Result and (i < PropCount) do
begin
Result := Result and (GetStrValueAt(i) = FirstVal);
Inc(i);
end;
end;
procedure TMixedFileNameProperty.Edit;
var
Dlg: TOpenDialog;
begin
Dlg := TOpenDialog.Create(Application);
try
with Dlg do
begin
Title := ’File for ’ + TComponent(GetComponent(0)).Name;
FileName:= Value;
if Execute then Value := FileName;
end;
finally
FreeAndNil(Dlg);
end
end;
function TMixedFileNameProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog]
end;
function TMixedFileNameProperty.GetValue: string;
begin
Result := GetStrValue;
end;
procedure TMixedFileNameProperty.SetValue(const Value: string);
begin
SetStrValue(Value);
end;
end.
把設計時代碼從運作時代碼中分離出去的最簡單方法是,把所有需要DesignIntf 和DesignEditors 代碼放入它們自己的單元中去,那個單元将要添加使用元件單元的聲明。元件自己不需要知道那個自己運作的IDE編輯器。首先,把DesignIntf 和 DesignEditors 單元從元件單元的Uses部分删除掉,然後讓編譯/連結器告訴你哪些類需要移到它們自己的單元中去:
[Error] MixedComponent.pas(23): Undeclared identifier: ’TPropertyEditor’
[Error] MixedComponent.pas(23): Class type required
[Error] MixedComponent.pas(24): Method ’AllEqual’ not found in base class
[Error] MixedComponent.pas(25): Method ’Edit’ not found in base class
[Error] MixedComponent.pas(26): Undeclared identifier: ’TPropertyAttributes’
[Error] MixedComponent.pas(27): Method ’GetValue’ not found in base class
[Error] MixedComponent.pas(28): Method ’SetValue’ not found in base class
[Error] MixedComponent.pas(35): Undeclared identifier: ’RegisterPropertyEditor’
[Error] MixedComponent.pas(35): Undeclared identifier: ’TMixedFile’
[Error] MixedComponent.pas(46): Undeclared identifier: ’GetStrValue’
[Error] MixedComponent.pas(49): Undeclared identifier: ’PropCount’
[Error] MixedComponent.pas(51): Undeclared identifier: ’GetStrValueAt’
[Error] MixedComponent.pas(51): Operator not applicable to this operand type
[Error] MixedComponent.pas(64): Undeclared identifier: ’GetComponent’
[Error] MixedComponent.pas(65): Undeclared identifier: ’Value’
[Error] MixedComponent.pas(75): Undeclared identifier: ’paDialog’
[Error] MixedComponent.pas(80): Undeclared identifier: ’GetStrValue’
[Error] MixedComponent.pas(85): Undeclared identifier: ’SetStrValue’
[Fatal Error] JOComponents.dpk(33): Could not compile used unit
’MixedComponent.pas’
下一步是建立一個新的單元存放這些代碼。可以命名為類似MixedComponentReg的名子。把Register函數也移到那個單元中去。下面我們可以從錯誤資訊中得知哪些需要移走。第一個錯誤資訊是[Error] MixedComponent.pas(23): Undeclared identifier: ’TPropertyEditor’,這個資訊指出了一個繼承自那個設計時單元的類。這是個很清楚的訓示,它指明了它是設計時代碼和這個類要被移到一個新建立的單元。
到此,運作時包将會被成功編譯(如果還不行,繼續把設計時代碼從單元中移去,直到沒有錯誤産生)。現在元件在你的應用程式運作時已不再需要Proxies.pas和其它設計時單元了。這個運作時元件非常簡單,如下:
unit MixedComponent;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
{ TMixedComponent }
TMixedComponent = class(TComponent)
private
FFileName: String;
published
property FileName : String read FFileName write FFileName;
{ Published declarations }
end;
implementation
end.
這最後一步就是把你的元件和它的屬性編輯器編譯到一個設計時包中,然後安裝到IDE中去。
通過File | New | Other 選擇 package建立一個新的包。調出包選項,選擇design-time only,給Description 項填一個描述文字。選擇Requires folder,點選Add按鈕。在Requires Package 編輯對話框填寫Designide.dcp,點選OK。同樣,為你的元件運作時包添加dcp。在這種情況下,它放在了JOComponents.dpk,是以JOComponents.dcp被添加到requires項。在requires裡有:JOComponents, designide 和 rtl。最後,選擇包含目錄,添加MixedComponentReg.pas 到包裡。
到現在我們已經基本完成了!打開MixedComponentReg.pas 添加一對單元到uses部分,這要看你的元件或屬性編輯器是否要在聲明中使用這個元件(一些複雜的編輯器有時需要知道這個元件的存在)。如果是這樣,把它加到Interface的uses部分。否則,加到implementation的uses部分。DesignIntf和DesignEditors就放到Interface的uses裡。SysUtils, Forms, Dialogs, 和 Classes 在内部的屬性編輯器的不同地方使用,是以就放到implementation部分。最後的MixedComponentReg應該象下面這樣:
unit MixedComponentReg;
interface
uses DesignIntf, DesignEditors;
type
{ TMixedFileNameProperty }
TMixedFileNameProperty = class(TPropertyEditor)
function AllEqual: boolean; override;
procedure Edit; override;
function GetAttributes: TPropertyAttributes; override;
function GetValue: string; override;
procedure SetValue (const Value: string); override;
end;
procedure Register;
implementation
uses MixedComponent, SysUtils, Forms, Dial