天天看點

關于把設計時代碼從運作時代碼中分離出來的問題

        自從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