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

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>
            国产精品亚洲综合天堂夜夜| 午夜精品一区二区三区四区 | 乱中年女人伦av一区二区| 亚洲精品欧美| 好吊一区二区三区| 国产精品超碰97尤物18| 欧美jizz19性欧美| 久久久精彩视频| 亚洲一区二区精品视频| 亚洲精品久久久久久久久| 久久婷婷国产麻豆91天堂| 亚洲女与黑人做爰| 一区二区三区四区五区视频| 亚洲福利视频一区二区| 国产视频亚洲精品| 国产精品久久久久久久久免费| 欧美69wwwcom| 久久视频在线视频| 久久国产精品久久久久久电车| 亚洲一级网站| 一区二区三区福利| 亚洲精选91| 亚洲另类在线一区| 亚洲国内在线| 亚洲激情一区| 亚洲国产cao| 亚洲国产精品第一区二区| 欧美大片免费久久精品三p| 久久永久免费| 久久久综合网站| 久久亚洲综合网| 久久久久国产精品一区| 久久久久久久网站| 久久亚洲精品视频| 久久综合久久美利坚合众国| 久久久五月婷婷| 久久亚洲精品视频| 免费在线观看日韩欧美| 欧美成人网在线| 亚洲国产另类久久精品| 亚洲大胆人体视频| 亚洲激情一区二区| 日韩午夜精品| 一本色道久久88综合日韩精品| 99视频热这里只有精品免费| 99热免费精品在线观看| 亚洲一级一区| 欧美与黑人午夜性猛交久久久| 久久精品国产亚洲高清剧情介绍| 久久国产精品亚洲va麻豆| 久久久人成影片一区二区三区| 久久免费观看视频| 欧美成人久久| 欧美日韩精品二区第二页| 欧美国产免费| 国产精品久久午夜| 国产视频在线观看一区| 精品电影一区| 999亚洲国产精| 欧美一二三区精品| 久久天天躁夜夜躁狠狠躁2022| 亚洲第一色在线| 亚洲欧美日韩另类| 日韩一级裸体免费视频| 欧美日韩国产成人在线免费| 久久综合网hezyo| 国产精品综合| 宅男精品视频| 亚洲欧美中文另类| 国产精品wwwwww| 国产精品99久久久久久www| 亚洲区在线播放| 久热精品在线视频| 亚洲丁香婷深爱综合| 亚洲精品中文在线| 欧美一区二区视频在线观看| 欧美99在线视频观看| 欧美午夜精品理论片a级大开眼界| 国产欧美欧洲在线观看| 亚洲高清资源| 欧美一级视频精品观看| 欧美成年人网站| 亚洲五月六月| 噜噜噜噜噜久久久久久91 | 亚洲欧美综合网| 美女脱光内衣内裤视频久久网站| 亚洲精品一区二区网址| 欧美一区=区| 欧美剧在线免费观看网站| 国产亚洲欧美在线| 99热这里只有精品8| 久久精品中文| 日韩一级片网址| 麻豆久久久9性大片| 国产欧美亚洲视频| 夜夜嗨av一区二区三区网页| 浪潮色综合久久天堂| 中文精品视频一区二区在线观看| 老司机aⅴ在线精品导航| 国产精品腿扒开做爽爽爽挤奶网站| 亚洲国产精品第一区二区三区| 欧美一二区视频| 99这里只有精品| 欧美mv日韩mv国产网站| 国内精品美女在线观看| 亚洲免费在线看| 亚洲欧洲三级电影| 麻豆国产精品777777在线| 国产欧美在线播放| 亚洲欧美中文日韩在线| 亚洲日本久久| 欧美jizz19hd性欧美| 在线观看日韩精品| 久久久精品五月天| 亚洲男女毛片无遮挡| 欧美日韩三级电影在线| 亚洲精品乱码久久久久久按摩观| 久久一区中文字幕| 欧美一级在线播放| 国产日韩欧美91| 午夜亚洲精品| 这里只有视频精品| 欧美日韩一区二区视频在线观看| 日韩视频一区二区三区| 亚洲国产成人av| 牛夜精品久久久久久久99黑人| 有码中文亚洲精品| 麻豆精品视频| 久久久久免费观看| 亚洲大胆av| 免费的成人av| 久久久欧美精品| 亚洲第一网站| 欧美激情一区二区三区四区 | 亚洲精品视频二区| 欧美激情女人20p| 免费精品视频| 亚洲精品久久视频| 亚洲人体一区| 欧美视频专区一二在线观看| 亚洲一区二区三区精品动漫| 这里只有精品视频在线| 国产精品夜夜夜| 久久国产精品第一页| 久久精品国产99| 亚洲黄色三级| 亚洲狼人精品一区二区三区| 欧美色图麻豆| 欧美在线视频日韩| 久久精品国产久精国产一老狼 | 亚洲激情在线播放| 亚洲电影在线| 欧美日韩国产在线观看| 亚洲欧美国产日韩天堂区| 亚洲欧美一区二区原创| 好吊日精品视频| 亚洲国产精品va在线看黑人 | 久久久人成影片一区二区三区| 亚洲大片一区二区三区| 亚洲国产婷婷香蕉久久久久久| 欧美三级电影一区| 久久精品一区二区三区不卡牛牛| 久久久精品2019中文字幕神马| 亚洲精品字幕| 亚洲一区自拍| 亚洲东热激情| 一区二区免费在线播放| 国模大胆一区二区三区| 亚洲激情成人网| 国产九色精品成人porny| 免费欧美电影| 欧美日韩你懂的| 久久久精品999| 欧美激情一区二区三区高清视频 | 一区二区精品在线观看| 国产一区亚洲| 亚洲欧洲一区二区在线播放| 国产老女人精品毛片久久| 欧美国产1区2区| 国产精品日韩在线一区| 欧美不卡一卡二卡免费版| 欧美体内she精视频| 久久一区二区精品| 欧美午夜一区二区三区免费大片| 久久久久久综合| 欧美日韩一区二区视频在线观看 | 中文精品99久久国产香蕉| 激情婷婷亚洲| 在线亚洲一区二区| 91久久精品国产| 午夜精品久久久久久久| 99re热这里只有精品免费视频| 性久久久久久久久久久久| 99国产精品久久久久老师| 久久成人国产精品| 亚洲一区二区三区四区视频| 另类春色校园亚洲| 久久精品亚洲精品| 欧美性色综合| 亚洲片在线观看|