青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Chip Studio

常用鏈接

統計

最新評論

Action高級開發

Action高級開發

Action開發篇

    在討論Action的開發前,我想先討論一下為什么要使用TActionList及TAction。從Delphi 4開始Borland提供了TActionList控件,ActionList提供了一種全新的設計用戶界面交互模式的方法。傳統的事件模式無法解決命令 狀態更新的問題,因為任何情況下命令都是有效的。Delphi 4通過使用ActionList及Action提供了新的方法來處理命令的實現,即命令的有效性問題。ActionList是一個非可視的控件里面包含了 一組TAction對象。兩者的關系有點像菜單項同菜單的關系。

圖3.18

    一個TAction對象提供一個命令,比如刪除一個目標的選項(例如刪除列表框的一個列表項),當Action控制的控件相應某些用戶的輸入會激發相應的 Action命令,通常是鼠標鍵盤點擊等動作。Action通常用來控制按鈕和菜單項這類控件,通過設定這類控件的Action屬性可以把兩者關聯起來。

    圖3.18顯示了一個關聯Action和控件的例子,EditCut1 Action被指定給SpeedButton1的Action屬性。當關聯完成后,Speedbutton的屬性會根據對應的EditCut1的屬性作出 相應的變化,比如按鈕的Caption會自動變成“Cu&t.”,當用戶點擊Cut按鈕時,由EditCut1 Action實現的相應命令就會被調用。當Memo1中有文本被選中的時候,Cut按鈕才是有效的,這是因為Delphi內置的EditCut Action實現了Action的OnUpdate事件,在那里對相關聯的Memo1進行了判斷,只有當Memo1中有文本被選擇了,Action對應剪 切操作才有效。

    參看前面在OTA部分實現的Winamp專家中,大量使用TAction對系統進行了管理,可以發現只需要在Action的OnUpdate事件中寫很少 的代碼甚至不寫代碼(對系統內置的Action而言),就可以對一些基本界面操作的有效性進行判斷,同時使用ActionList容器類使得命令更容易維 護。另外,除了以上功能,還可以利用Action的OnHint事件對關聯控件的飛躍提示的顯示進行控制。

    雖然Action的使用是非常方便的,但如果想得到最佳的性能和表現,我們還是需要更深入地了解Action的工作原理。比如注意到 TActionList和TAction都定義了OnExecute和OnUpdate事件。兩者有什么區別呢?在Borland的文檔中并沒有很清楚的 說明,所以還是需要研究一下Action工作的內部機制。

    當在程序中使用了ActionList和Action后,Delphi 中的Application對象會在系統空閑的時候產生OnUpdate事件。對于每一個ActionList, TActionList.OnUpdate事件會最先生成,然后系統傳遞給事件兩個參數。Action參數代表正在更新狀態的Action, Handled參數用來控制相應的Action的OnUpdate事件是否被調用。如果不想相應的Action的Update事件被調用,需要設定 Handled參數為真。

    TActionList.OnUpdate事件對每一個列表中的Action相關聯的每一個控件都要產生一遍。換句話,假設ActionList1包含 Action1和Action2。現在假定Button1和SpeedButton1的 Action 屬性設定為Action1,同時SpeedButton2的Action屬性設定為Action2。在這種情況下,每個循環下來, ActionList1.OnUpdate事件將會產生1+2=3次。

    什么時候用Action.OnUpdate事件,什么時候用ActionList.OnUpdate事件沒有什么絕對的準則。但一般來說, TActionList.OnUpdate事件更容易控制,因為把全部的狀態控制代碼寫在一個地方更清楚,而且寫起來更簡潔,當然這只是我的看法。

    另外,大家可能會注意到可以在一個窗體上放多個ActionList。比如Delphi帶的RichEdit的演示程序中就使用了兩個 ActionList,為什么使用兩個ActionList呢?其實這是出于運行效率的考慮,因為按照ActionList更新狀態的方式,如果你有一組 Action并不需要進行有效性校驗。這時就應該把它們放到單獨的ActionLis中去,這樣就不需要生成OnUpdate事件了。這是因為不管 Action是否生成了OnUpdate事件,只要ActionList中有一個Action定義了OnUpdate事件,其他Action的 OnUpdate都會被調用。

    同時由于OnUpdate事件會產生很多次,所以不要在OnUpdate事件處理函數中寫耗時很長的代碼,這樣會嚴重影響應用程序的運行效率。

圖3.19

    同OnUpdate事件相反,我推薦為每一個Action對象生成一個OnExecute的事件,而不是全部寫到 TActionList.OnExecute事件中去,它不同于OnUpdate事件之處在于只有當相應操作需要執行時,事件才會被調用,而 OnUpdate事件是只要系統空閑就會被調用。

    在Delphi的在線幫助中,關于Action執行過程中事件產生的順序有點模糊不清。圖3.19顯示了一個更加清晰的順序圖。注意Action的 OnExecute事件發生在ActionList和Application 對象獲得一個機會去處理Action之后。

    除了OnUpdate和OnExecute事件外,TActionList還定義了OnChange事件,這是一個非常奇怪的事件,只有當Action的 Category屬性改變的時候或者是當ActionList的Images屬性改變的時候才會被調用,我不清楚有什么必要生成這么樣一個事件,因為 Category只是在設計時才有效,Images也極少在運行時改變,所以我覺得這個事件定義的好像沒有必要。相比之下,OnHint事件更有用些,生 成一個OnHint事件處理過程使我們可以比較容易定制要顯示的飛躍提示。OnHint事件有兩個參數:第一個是HintStr,用它來返回要顯示的提示 字符串;第二個參數CanShow用來確定是否顯示提示。這個事件處理有點問題,在無焦點的控件如SpeedButton定義的OnHint事件中, CanShow參數只在Action的Hint屬性設置為空字符串的情況下才有效,如果Hint屬性指定了一個字符串,那么不管CanShow如何設置, 控件只會顯示缺省的Hint字符串。

    Action還具有給菜單項或SpeedButton添加圖標的功能。但要注意僅僅設定TActiongList.Images屬性是不夠的,還需要同時 設定Menu的Images屬性為相同的圖像列表。另外假設我們要修改同Action關聯的圖像,僅僅修改對應的圖像列表是不夠的,不像Action的其 他屬性(如:Caption或ShortCut),只要修改了,就會通知相應控件作出改變。我們必須先清除對應控件的Action屬性,在重置 Action屬性達到刷新控件的Glyph屬性的目的。

    如果不想讓Action的圖像出現在對應的SpeedButton上時,我們必須在運行時(如在窗體的OnCreate事件中)清除SpeedButton的Glyph屬性,在設計時清空Glyph屬性是無效的。

    下一節將探討如何重用現有Action源代碼來實現新的Action類。

設計新武器 ——Action的開發

    這節將探討如何編寫新的Action。

    首先,必須清楚Action是控件,我們可以像寫其它VCL控件一樣,編寫并能注冊它。

    其次,編寫新的Action是有一定前提條件的,要編寫的Action必須是對應于比較普遍的操作,可重用性要強,比如從一個列表框中刪除列表項的操作以 及上下移動列表項都是在界面交互時會經常碰到的需求。當然通過在普通的TAction的OnExecute和OnUpdate事件中也可以實現這類操作, 但如果能通過編寫對應于這樣操作的Action,就可以省去重復編寫OnUpdate和OnExecute事件處理過程的工作。

圖3.20

    另外要弄清,要編寫的Action不同于一般意義上的Action的,我們定制的Action是用戶可以不需要寫OnExecute事件處理過程的,它提 供了一個內置的缺省功能,就好像TEditCutAction一樣,只要把它同編輯框相關聯,無需寫一行代碼它就可以正確處理剪切操作了。同樣對于 OnUpdate事件,用戶定制的Action也提供了內置的命令有效性校驗機制,用戶可以無需修改直接使用。但最重要的區別恐怕是用戶定制的 Action可以用于很多不同的控件并能在不同程序中重用。

1. 預定義的Action

    在開始編寫定制的Action前,先來看看Delphi已經實現了的定制的Action。圖3.20中列出了Delphi自帶的標準Action, Edit Action處理剪貼板操作,Window Action處理多文檔界面(MDI)的子窗體的管理操作,DataSet Action則處理數據庫導航命令。

    預定義的Action提供了很強大的功能。比如假定把一個SpeedButton的Action屬性同一個TEditCut Action相關聯,當任何編輯框中的文本被選擇后,SpeedButton就處于有效狀態,這時點擊SpeedButton,被選的文本將被刪除并復制 到剪貼板上。所有這些功能不需要寫任何代碼。

    雖然預定義的Action功能很強大,但還是有一些應用上的限制。為了這些限制存在的原因,我們需要了解定制的Action是如何定位操作目標控件的。比 如當一個TEditCut Action 被激發的時候,它是如何知道操作是在Memo1上而不是在Edit2上的,也就是如何區分需要剪切的控件的?

2. 定位目標

    回憶一下前面講過的,當一個Action被激發時,有4種可能的響應會發生:第一,Action List在它的OnExecute事件中處理Action;第二,Application對象會處理Action;第三,Action調用本身的 OnExecute事件處理過程;如果這時Action還沒有被處理,一個cm_ActionExecute 消息被發送到Application對象。

    當Application對象接收到這個消息,它首先把消息發到Screen對象管理的當前激活的窗體,如果當前沒有活動的窗體,消息就發給應用程序的主窗體。

圖3.21

    消息由TCustomForm.CM-ActionExecute 消息處理過程進行處理。首先,處理過程檢查窗體的ActiveControl屬性。如果不為nil,當前活動控件的ExecuteAction方法就會被 調用,如果ActiveControl屬性為nil,窗體的ExecuteAction方法會被調用(見圖3.21)。

    ExecuteAction是一個布爾函數,定義在TComponent類中。如果控件對Action做出了響應,ExecuteAction 函數就返回真值。ExecuteAction函數會調用Action的HandlesTarget方法,并把它的引用參考傳給控件。 HandlesTarget方法決定控件是否是Action的一個有效的操作對象。比如TEditAction對象HandlesTarget只有當目標 控件是從TCustomEdit繼承下來的時候才返回真值。

    如果控件是一個有效的操作對象,Action的ExecuteTarget方法將被調用,同HandlesTarget類似,它接收一個目標控件的引用參 考作為一個參數,ExecuteTarget方法對目標控件執行相應的Action。圖3.21舉例說明了這一處理流程。

    如果ActiveControl和窗體都不是一個有效的操作目標,這種情況下,窗體的CM-ActionExecute方法會遍歷窗體上的全部控件并對每一個控件都調用ExecuteAction方法直到一個有效的目標控件被找到或是找不到滿足要求的控件為止。


3. 定制Action的局限性

    理解了定制的Action如何定位它的目標控件,就可以討論它們的局限性了。第一個局限來自于對ActiveControl的依賴性。因為當激發 Action的控件改變了輸入焦點的時候,定制的Action可能無法正常工作,這對于SpeedButton和菜單項是沒有影響的,因為它們沒有輸入焦 點,不會改變ActiveControl。然而普通的按鈕卻會改變輸入焦點。

    假定Button1的Action屬性設定為EditCut1。同時假定Edit1當前獲得了焦點,并且其中的文本被選擇了。當用戶點擊了 Button1,發生的第一件事是輸入焦點切換到了按鈕上。這時, EditCut1.OnUpdate事件被自動調用。因為現在Button1成了ActiveControl,而它并不是TCustomEdit的子類, EditCut1.Enabled的屬性將設為False。結果Action變成無效的了, Button1也同樣失效了。當用戶松開鼠標后,由于Button1無效了,結果OnClick事件就無法調用了,也就無法激發EditCut1的操作 了。

    這里還有一些其他限制,特別當這些編輯操作只能工作在TCustomEdit子類上時,它無法支持很多支持剪貼板操作的其他控件。比如,Edit Action不能工作在csDropDown樣式的組合列表框,而且在設計時,無法控制Edit Action只對某個編輯框起作用。它只對所有的控件起作用(不過在運行時倒是可以通過TeditAction的Control屬性進行控制)。

    指出這些限制并不會影響這些Action的重要性,這只是為了理解整個Action的工作流程。

4. 創建用戶定制的Action

    創建Action同創建VCL控件非常類似,其實這并不奇怪,因為Action實際上就是控件的一種。編寫控件的規則同樣適用于Action。不過編寫Action之前,先看一下Action類的繼承關系。

    圖3.22是非數據庫Action的繼承關系。編寫新的Action,通常是從TAction開始的,但也應該了解一下全部的4個Action基類之間的 關系: TBasicAction、TContainedAction、TcustomAction和TAction。

    TBasicAction是最低層的類,如果想要創建一個同非菜單或控件相關的Action,可以由它開始繼承。

    TContainedAction是直接從TbasicAction繼承的類,增加了分類的功能,支持TActionList中的分類(Category)。

    TCustomAction直接從TContainedAction繼承下來,增加了針對菜單項和控件的功能, TcustomAction沒有公開它的屬性,僅僅是實現了它們。

    TAction僅僅是公開了TcustomAction實現了的屬性。它沒有引進新的功能,除非需要使特定的屬性隱藏,通常Action都是從TAction繼承的。

l                      

圖3.22

5. 定制Action

    現在就可以開始編寫Action了,創建一個定制的Action包括以下幾個步驟:

    (1) 需要創建一個新單元,在單元中要從一個基類(比如TAction)繼承我們的Action。

    (2) 然后,需要重載一些關鍵的方法像HandlesTarget、UpdateTarget和ExecuteTarget方法。

    (3) 最后注冊新的Action把它安裝到Delphi中去。

    在線幫助建議使用StdActns單元作為創建定制的Action的一個指導。但是如果按照StdActns單元來寫的話,生成的Action同 Delphi預定義的Action工作的方式無法完全一樣。比如,用Action List Editor創建一個TEditPaste Action,然后選擇這個Action,對象編輯器顯示的Caption已經初始化為“&Paste”,提示初始化為“Paste”、 ImageIndex為2以及ShortCut為Ctrl+V。而生成的Action卻無法做到對缺省屬性值的初始化。

    如果創建一個基于StdActns單元中代碼的Action,唯一能夠初始化的屬性是Caption,而且這個Caption被初始化為同Name一樣的 無意義的值,像“MyAction1”這樣無聊的名字。造成這種情況的根本原因將在后面的Action的安裝部分講到,這里通過提供一個 Constructor來初始化缺省的屬性值。

    另外,StdActns單元比較糟糕的地方是在這些單元中并沒有如何注冊和安裝Action的內容。Action雖然是一種控件,但它的注冊同普通控件是不同的。更離譜的是在線幫助中關于如何注冊Action是錯誤的。

6. 列表框Action

    雖然StdActns單元不是一個很好的指導文件,但只能從它開始研究。下面將創建一組Action用來提供對列表框的操作。后面的程序清單1顯示了實現這組Action的源代碼。

    類似標準的Edit Action,從一個TListBoxAction基類開始處理目標控件的確定過程。這個類重載了HandlesTarget和UpdateTarget 方法。如果目標控件是TCustomListBox的子類,HandlesTarget方法將返回真值,如果列表框不是空的,有列表項存在, UpdateTarget方法設定Action的Enabled屬性為真。相應代碼如下:

    function TListBoxAction.HandlesTarget( Target: TObject ): Boolean;

    begin

      //當Target為TCustomListbox的子類時,相應的命令才有效

      Result := ( ( Control <> nil ) and ( Target = Control ) or

              ( Control = nil ) and ( Target is TCustomListBox ) ) and

              TCustomListBox( Target ).Focused;

    end;

    procedure TListBoxAction.UpdateTarget( Target: TObject );

    begin

      //當相應的列表框不為空,包含列表項時,命令才有效

      Enabled := GetControl( Target ).Items.Count > 0;

    end;

    TlistBoxAction剩下的部分用來支持Control屬性。 TeditAction同樣實現了一個Control 屬性,可以使Action只對某一控件起作用,不過它的缺點是它聲明為public屬性,這樣只能在運行時才能對它進行設置。這里聲明Control為 published屬性,這樣在設計時就可以方便地進行設置了。不設定Control屬性的話,Action將作用于窗體上全部的列表框。

    所有的派生類在結構上都比較類似,他們從同一個基類繼承并且都重載了Constructor來初始化自身屬性,還都重載了ExecuteTarget方法 來實現內置的執行功能。下面是TListBoxDelete的ExecuteTarget方法的實現,用來刪除當前被選的列表項:

    procedure TListBoxDelete.ExecuteTarget(Target: TObject);

    var

      Idx: Integer;

    begin

      Idx := GetControl( Target ).ItemIndex;

      if Idx <> -1 then

        GetControl( Target ).Items.Delete( Idx );

    end;

    一部分子類還重載了UpdateTarget方法,因為確定命令是否有效的規則對不同操作是不同的,同在TListBoxAction中實現的有可能不 同。例如,TListBoxMoveUp.UpdateTarget方法 判斷Enabled屬性是否為真是以被選的列表項是否是列表中的第一項為依據的,如果為第一項則上移的操作是無效的。代碼示意如下:

    procedure TListBoxMoveUp.UpdateTarget( Target: TObject );

    begin

      Enabled := GetControl( Target ).ItemIndex > 0;

    end;

7. 注冊和安裝Action

    完成Action的定義和編碼后,需要注冊安裝Action到Delphi。這里使用一個單獨的單元來進行注冊。清單2列出了注冊單元代碼。

    同一般控件不同,注冊Action我們需要使用RegisterActions過程,而不是RegisterComponents過程。 RegisterActions過程需要三個參數:第一個是一個描述Action分類的字符串,由于Action是專門針對列表框的,所以設定這個參數為 “ListBox.”;第二個參數是一組要注冊的Action的類;最后一個參數稱為Resource參數,這個參數在在線幫助里沒有提到,它的類型是 TComponentClass,稍后會詳細研究這個參數的具體用意,這里先不管它,把它直接設成TlistBox就可以。

圖3.23

    因為Action實際上是一種控件,要安裝的話,必須把實現的單元放到包中然后進行注冊。一旦安裝到了Delphi里,新定制的Action就會出現在標準Action對話框中,如圖3.23所示。

    最后還剩下一點問題那就是因為TCustomAction.DoHint(提示事件分發方法)在Delphi 4中聲明為靜態方法,而在Delphi 5聲明為動態方法。這樣在Delphi 5中就可以對提示處理進行重載,提供一個用戶定制的提示處理,而在Delphi 4這是辦不到的。本文的例子沒有實現提示的定制部分,這個問題留給讀者去實現。

    程序清單1 – ListActn.pas如下:

    unit ListActn;

    interface

    uses

      Classes, ActnList, StdCtrls;

    type

      TListBoxAction = class( TAction )

      private

        FControl: TCustomListBox;

        procedure SetControl( Value: TCustomListBox );

      protected

        function GetControl( Target: TObject ): TCustomListBox; virtual;

        procedure Notification( AComponent: TComponent; Operation: TOperation ); override;

      public

        function HandlesTarget( Target: TObject ): Boolean; override;

        procedure UpdateTarget( Target: TObject ); override;

      published

        property Control: TCustomListBox

          read FControl

          write SetControl;

      end;

      TListBoxDelete = class( TListBoxAction )

      public

        constructor Create( AOwner: TComponent ); override;

        procedure UpdateTarget( Target: TObject ); override;

        procedure ExecuteTarget( Target: TObject ); override;

      end;

      TListBoxClear = class( TListBoxAction )

      public

        constructor Create( AOwner: TComponent ); override;

        procedure ExecuteTarget( Target: TObject ); override;

      end;

      TListBoxMoveUp = class( TListBoxAction )

      public

        constructor Create( AOwner: TComponent ); override;

        procedure UpdateTarget( Target: TObject ); override;

        procedure ExecuteTarget( Target: TObject ); override;

      end;

      TListBoxMoveDown = class( TListBoxAction )

      public

        constructor Create( AOwner: TComponent ); override;

        procedure UpdateTarget( Target: TObject ); override;

        procedure ExecuteTarget( Target: TObject ); override;

      end;

      TListBoxSelectAll = class( TListBoxAction )

      public

        constructor Create( AOwner: TComponent ); override;

        procedure ExecuteTarget( Target: TObject ); override;

      end;

      TListBoxUnselectAll = class( TListBoxAction )

      public

        constructor Create( AOwner: TComponent ); override;

        procedure ExecuteTarget( Target: TObject ); override;

      end;

    implementation

    uses

      Windows, Messages;

      {== TListBoxAction Methods ==}

    function TListBoxAction.GetControl( Target: TObject ): TCustomListBox;

    begin

      Result := Target as TCustomListBox;

    end;

    function TListBoxAction.HandlesTarget( Target: TObject ): Boolean;

    begin

      Result := ( ( Control <> nil ) and ( Target = Control ) or

        ( Control = nil ) and ( Target is TCustomListBox ) ) and

        TCustomListBox( Target ).Focused;

    end;

    procedure TListBoxAction.Notification( AComponent: TComponent; Operation: TOperation );

    begin

      inherited Notification( AComponent, Operation );

      if ( Operation = opRemove ) and ( AComponent = Control ) then

        Control := nil;

    end;

    procedure TListBoxAction.UpdateTarget( Target: TObject );

    begin

      Enabled := GetControl( Target ).Items.Count > 0;

    end;

    procedure TListBoxAction.SetControl( Value: TCustomListBox );

    begin

      if Value <> FControl then

      begin

        FControl := Value;

        if Value <> nil then

          Value.FreeNotification( Self );

      end;

    end;

    {== TListBoxDelete Methods ==}

    constructor TListBoxDelete.Create( AOwner: TComponent );

    begin

      inherited Create( AOwner );

      Caption := 'Delete';

      ImageIndex := 1;

      Hint := 'Delete Item';

    end;

    procedure TListBoxDelete.ExecuteTarget(Target: TObject);

    var

      Idx: Integer;

    begin

      Idx := GetControl( Target ).ItemIndex;

      if Idx <> -1 then

        GetControl( Target ).Items.Delete( Idx );

    end;

    procedure TListBoxDelete.UpdateTarget( Target: TObject );

    begin

      Enabled := ( GetControl( Target ).Items.Count > 0 ) and

                 ( GetControl( Target ).ItemIndex <> -1 );

    end;

    {== TListBoxClear Methods ==}

    constructor TListBoxClear.Create( AOwner: TComponent );

    begin

      inherited Create( AOwner );

      Caption := 'Clear';

      ImageIndex := 2;

      Hint := 'Clear List';

    end;

    procedure TListBoxClear.ExecuteTarget( Target: TObject );

    begin

      GetControl( Target ).Clear;

    end;

    {== TListBoxMoveUp Methods ==}

    constructor TListBoxMoveUp.Create( AOwner: TComponent );

    begin

      inherited Create( AOwner );

      Caption := 'Move Up';

      ImageIndex := 3;

      Hint := 'Move Item Up';

    end;

    procedure TListBoxMoveUp.ExecuteTarget( Target: TObject );

    var

      Idx: Integer;

    begin

      Idx := GetControl( Target ).ItemIndex;

      GetControl( Target ).Items.Exchange( Idx, Idx - 1 );

      GetControl( Target ).ItemIndex := Idx - 1;

    end;

    procedure TListBoxMoveUp.UpdateTarget( Target: TObject );

    begin

      Enabled := GetControl( Target ).ItemIndex > 0;

    end;

    {== TListBoxMoveDown Methods ==}

    constructor TListBoxMoveDown.Create( AOwner: TComponent );

    begin

      inherited Create( AOwner );

      Caption := 'Move Down';

      ImageIndex := 4;

      Hint := 'Move Item Down';

    end;

    procedure TListBoxMoveDown.ExecuteTarget( Target: TObject );

    var

      Idx: Integer;

    begin

      Idx := GetControl( Target ).ItemIndex;

      GetControl( Target ).Items.Exchange( Idx, Idx + 1 );

      GetControl( Target ).ItemIndex := Idx + 1;

    end;

    procedure TListBoxMoveDown.UpdateTarget( Target: TObject );

    var

      L: TCustomListBox;

    begin

      L := GetControl( Target );

      Enabled := ( L.ItemIndex <> -1 ) and

             ( L.ItemIndex < L.Items.Count - 1 );

    end;

    {== TListBoxSelectAll Methods ==}

    constructor TListBoxSelectAll.Create( AOwner: TComponent );

    begin

      inherited Create( AOwner );

      Caption := 'Select All';

      ImageIndex := 5;

      Hint := 'Select All Items';

    end;

    procedure TListBoxSelectAll.ExecuteTarget( Target: TObject );

    begin

      SendMessage( GetControl( Target ).Handle, LB_SETSEL, 1, -1 );

    end;

    {== TListBoxUnselectAll Methods ==}

    constructor TListBoxUnselectAll.Create( AOwner: TComponent );

    begin

      inherited Create( AOwner );

      Caption := 'Unselect All';

      ImageIndex := 6;

      Hint := 'Unselect All Items';

    end;

    procedure TListBoxUnselectAll.ExecuteTarget( Target: TObject );

    begin

      SendMessage( GetControl( Target ).Handle, LB_SETSEL, 0, -1 );

    end;

    end.

    程序清單2 - ListActnReg.pas如下:

    unit listActnReg;

    interface

    procedure Register;

    implementation

    uses

      Classes, ActnList, StdCtrls,ListActn;

    procedure Register;

    begin

      RegisterActions( 'ListBox', [ TListBoxDelete, TListBoxClear, TListBoxMoveUp, TListBoxMoveDown,

                   TListBoxSelectAll, TListBoxUnselectAll ], TListBox );

    end;

    end.

    Resource 參數:

    上面留了一個問題,就是Resource參數到底是干什么用的?現在是該揭開謎底的時候了。

    其實Resource參數可以為我們的Action屬性初始化,并且可以在Action中嵌入指定的圖像。實際上就是起到了Constructor過程的作用,它還能給Action添加缺省圖標。下面就是使用Resource參數的步驟:

    (1)給原來的安裝包添加一個資源窗體。

    (2)重新編譯和安裝Action。

    第一步,添加一個新的窗體,把它命名為TListBoxRes。然后添加一個圖像列表控件,一個ActionList并添加我們先前創建的Action。 最后,向ImageList中添加圖標,并設定Action的各項缺省屬性,包括圖標、Capition等等,最后把它保存為ListRes.Pas。

    第二步,因為要使用Resource參數來重新注冊,這回系統會根據資源窗體的內容設定Action的缺省屬性。這時就不再需要Action中的 Constructor過程了(這回終于明白為什么Borland的StdActns單元中實現的Action都沒有constructor過程),把這 些過程去掉。并把先前RegisterAction中Resource參數的Tlistbox改成TlistBoxRes,然后把TlistBoxRes 對應的單元添加到注冊單元的Uses列表中,下面是修改后的注冊部分代碼:

    uses

      Classes, ActnList, StdCtrls,ListActn,ListRes;

      ……………………………………………

    procedure Register;

    begin

圖3.24

      RegisterActions( 'ListBox', [ TListBoxDelete, TListBoxClear, TListBoxMoveUp, TListBoxMoveDown, TListBoxSelectAll, TListBoxUnselectAll ], TListBoxRes );

    end;

    最后重新編譯包。如果安裝成功的話,讓我們來測試一下。新建一個窗體,添加一個ActionList,再放上一個TimageList控件,指定 ActionList的Images屬性為Imagelist1。然后添加Listbox Action,如圖3.24所示,你會發現Action的右邊都有一個漂亮的圖標,并且在沒有Contructor的情況下,Action的屬性都設定了 正確的缺省屬性值。除此之外,即使ImageList中已經有了圖像,Delphi也會自動復制Action資源中的圖標到Imagelist,并能很智 能地調節Action的ImageIndex。到此為止,我們才算真的大功告成了。


posted on 2008-02-06 02:45 MyChip 閱讀(256) 評論(0)  編輯 收藏 引用

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲欧美在线看| 国产精品欧美久久久久无广告| 国产乱子伦一区二区三区国色天香| 日韩一级欧洲| 91久久精品美女高潮| 亚洲欧美精品伊人久久| 亚洲黄色性网站| 美女91精品| 亚洲精品你懂的| 久久久亚洲精品一区二区三区 | 欧美国产成人精品| 久久综合狠狠| 亚洲欧洲美洲综合色网| 亚洲永久视频| 欧美一区二区在线| 激情欧美日韩一区| 亚洲高清在线视频| 欧美aⅴ一区二区三区视频| 亚洲视频在线看| 欧美香蕉视频| 国产色产综合色产在线视频| 亚洲已满18点击进入久久| 亚洲天堂成人在线观看| 亚洲二区在线| 亚洲精品一级| 欧美福利电影网| 日韩亚洲欧美中文三级| 亚洲午夜久久久久久久久电影院| 久久精品国产999大香线蕉| 男女精品网站| 欧美一区二区三区在线观看视频| 精品av久久707| 亚洲一区二区三区在线| 一区二区三区欧美在线观看| 国产精品mv在线观看| 在线视频欧美精品| 欧美黄色视屏| 美女日韩欧美| 日韩午夜在线观看视频| 性欧美超级视频| 欧美中文字幕第一页| 欧美18av| 欧美亚洲在线播放| 国产一区二区三区久久久久久久久| 亚洲一区二区三区四区在线观看| 夜夜嗨av一区二区三区免费区| 国产精品普通话对白| 一本色道久久88综合亚洲精品ⅰ| 亚洲午夜精品在线| 亚洲午夜女主播在线直播| 亚洲激情影视| 国产精品久久久久av| 欧美sm极限捆绑bd| 久久在线免费观看| 亚洲国产精品成人综合色在线婷婷| 久久久一本精品99久久精品66| 欧美剧在线免费观看网站| 欧美黄免费看| 午夜国产欧美理论在线播放| 午夜免费电影一区在线观看| 亚洲精选视频在线| 欧美激情欧美激情在线五月| 欧美日韩国产电影| 在线视频精品一区| 欧美成人xxx| 久久精品国产久精国产爱| 蜜臀久久久99精品久久久久久 | 亚洲综合色网站| 亚洲国产精品www| 亚洲国产国产亚洲一二三| 欧美福利网址| 亚洲最新视频在线播放| 久久躁日日躁aaaaxxxx| 免费观看久久久4p| 亚洲高清视频的网址| 欧美a一区二区| 亚洲精品三级| 亚洲乱码国产乱码精品精天堂 | 欧美一级大片在线观看| 国产精品久久综合| 久久精品亚洲| 亚洲精品久久久久久下一站| 国产麻豆精品theporn| 欧美日韩成人在线播放| 亚洲视频 欧洲视频| 欧美国产成人精品| 午夜视频一区二区| 亚洲人体大胆视频| 欧美黑人多人双交| 欧美视频在线一区二区三区| aa级大片欧美| 午夜一区二区三视频在线观看 | 这里只有精品在线播放| 91久久久亚洲精品| 国产欧美一区在线| 日韩亚洲欧美一区二区三区| 老司机午夜免费精品视频| 亚洲国产成人在线播放| 欧美激情成人在线| 国色天香一区二区| 亚洲网站在线播放| 一本综合精品| 久久这里只精品最新地址| 日韩网站在线观看| 久久久久国产精品厨房| 久久爱另类一区二区小说| 性欧美video另类hd性玩具| 亚洲欧美在线播放| 欧美影院在线| 一区二区日韩精品| 亚洲精品久久视频| 午夜精品一区二区三区在线视| 欧美在线精品一区| 玖玖视频精品| 亚洲特黄一级片| 9人人澡人人爽人人精品| 国产一区二区三区的电影| 国内精品福利| 一本色道久久综合亚洲精品不卡 | 亚洲欧洲日本一区二区三区| 国产精品欧美一区喷水| 亚洲乱码国产乱码精品精98午夜| 欧美黄网免费在线观看| 西瓜成人精品人成网站| 欧美jizz19hd性欧美| 韩国三级在线一区| 亚洲婷婷免费| 亚洲欧洲在线免费| 亚洲精品久久久久中文字幕欢迎你| 另类酷文…触手系列精品集v1小说| 亚洲一区在线看| 国产精品www994| 欧美劲爆第一页| 亚洲人成亚洲人成在线观看图片 | 另类综合日韩欧美亚洲| 国产精自产拍久久久久久| 99在线精品视频| 性做久久久久久久免费看| 亚洲精品在线视频观看| 欧美日韩91| 国产综合色在线视频区| 久久成人免费网| 免费看黄裸体一级大秀欧美| 亚洲免费大片| 亚洲私人影院在线观看| 国产精品video| 合欧美一区二区三区| 久久九九精品| 免费亚洲视频| 午夜精品久久久久久久久久久| 欧美成人精品1314www| 亚洲国产精品电影| 亚洲视频图片小说| 久久久最新网址| 久久综合免费视频影院| 国产精品一区二区你懂得 | 久久成人亚洲| 韩国三级在线一区| 久久人人精品| 亚洲第一福利社区| 亚洲精品一区二区三区福利| 欧美激情亚洲精品| 一区二区三区成人| 性色av一区二区三区在线观看| 国产精品婷婷午夜在线观看| 午夜一区不卡| 欧美 亚欧 日韩视频在线| 欧美精品18+| 日韩视频一区二区| 香蕉av777xxx色综合一区| 国产一区二区三区四区在线观看| 久久噜噜亚洲综合| 亚洲区国产区| 欧美在线一二三四区| 午夜视频在线观看一区二区三区 | 一区二区三区偷拍| 欧美一区激情| 亚洲日本电影| 国产精一区二区三区| 另类欧美日韩国产在线| 在线午夜精品| 欧美电影免费观看| 亚洲欧美日韩在线高清直播| 黑人中文字幕一区二区三区| 欧美啪啪一区| 久久久久久电影| 99天天综合性| 欧美黄在线观看| 欧美在现视频| 一本高清dvd不卡在线观看| 欧美在线观看视频| 亚洲日本乱码在线观看| 久久久久久69| 午夜精品一区二区三区在线视 | 亚洲色图综合久久| 精品动漫3d一区二区三区| 欧美午夜精彩| 欧美激情综合| 久久亚洲欧美| 久久国产一二区|