程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 組件制作之五(托盤組件)

組件制作之五(托盤組件)

編輯:Delphi
這將是最後一個組件了,目標定為非可視化,事實上非可視化組件要比可視化組件難做,因為是從TComponent繼承而來,就沒有了很多屬性和事件。而這些都要我們從頭來做過。
  
  這個非可視化組件,我決定為托盤組件,其中用到的技術較多,我不如列一個表出來,然後再來講解好一點。另外,可能篇幅會多一些,請耐心看。
  
  用到的技術:
  
  1作為核心功能,當然是托盤的應用啦。
  
  2?托盤組件怎麼樣影響到主窗口最小化時隱藏
  
  3?托盤如何處理消息
  
  4?組件編輯器的用法
  
  上面每一個技術都非常有趣,讓我們一個個來看吧:
  
  ?
  
  一??托盤,是系統殼編程的一個功能,相信我們也看過很多啦,大概知道它用起來是什麼樣子的。
  
  那麼它是如何實現的呢,
  
  Windows定義了這樣一個結構來存放托盤的信息:
  
  typedef?struct?_NOTIFYICONDATA?{?//?nid??
  
  DWord?cbSize;
  
  ????HWND?hWnd;?
  
  ????UINT?uID;?
  
  ????UINT?uFlags;?
  
  ????UINT?uCallbackMessage;?
  
  ????HICON?hIcon;?
  
  ????char?szTip[64];?
  
  }?NOTIFYICONDATA,?*PNOTIFYICONDATA;
  
  cbSize是NOTIFYICONDATA結構的尺寸,我們一般用Sizeof就可以了
  
  hWnd一個窗口句柄,用於檢索托盤消息的。然而我們的非可視組件並沒有窗口呀,這就是技術列表第三條要講的,這裡從略
  
  uID?唯?一標識托盤圖標的,我們可以隨便指定一個數,但如果同時有不同的圖標,則數應該不同
  
  uFlags是NIF_ICON,NIF_MESSAGE,NIF_TIP中的一個或多個,我們全用就可以了。
  
  uCallbackMessage;托盤消息,是我們自定義的消息,這裡我們定義為:
  
  ????????????????const
  
  ??????????????????WM_TrayMsg=WM_USER+10;
  
  hIcon托盤圖標句柄
  
  szTip這個是托盤提示,當托盤出現時,鼠標移到哪裡,就會出現該提示。
  
  Delphi將這個結構重定義為TNotifyIconData,我們照這個來用就行了
  
  ?
  
  我們應用托盤要用到API函數Shell_NotifyIcon,其中有兩個參數,第一個為
  
  NIM_ADD,NIM_DELETE??,NIM_MODIFY中的一個,分別表示添加托盤(圖標出現)
  
  修改托盤(比如圖標,提示),刪除(圖標消失)第二個參數是NOTIFYICONDATA的指針
  
  嗯,托盤應該差不多了。
  
  ?
  
  二?這個組件能夠決定主窗體最小化時,是否是正常最小化並沒有托盤圖標。還是最小化到屏幕之外,使我們看不見,且托盤區出現了圖標。這裡有一個成員為FActive來決定。
  
  那麼我們是怎麼樣影響到主窗體呢,也即怎麼截獲窗體的最小化消息呢。
  
  全局變量Application有一個方法為procedure?HookMainWindow(Hook:?TWindowHook);
  
  顧名思義,就是鉤到主窗口的所有消息。裡面的參數是TWindowHook類型,它是一個方法指針,定義如下:
  
  type?TWindowHook?=?function(var?Message:?TMessage):?Boolean?of?object;
  
  我們要自己定義過程的,然後傳給HookMainWindow:
  
  function?AppMsgHook(var?Msg:TMessage):Boolean;
  
  Application.HookMainWindow(AppMsgHook);
  
  這樣做之後,主窗口的所有消息都會經過AppMsgHook方法啦,最小化消息也不例外,則我們可以在裡面截獲這個消息,並做一些操作:
  
  ?
  
  做什麼操作呢,先判斷組件是否為設計時,如果是,不進行操作,如果不是進行下一步
  
  if?not?(csDesigning?in?ComponentState)?then
  
  這樣的意圖是很明顯的,因為當設計時的主窗其實是Delphi的IDE,如果讓他處理該消息,其實是處理IDE的最小化消息,這時如果你最小化IDE,就會出現托盤啦。所以不能。
  
  ?
  
  下一步是是否截獲了最小化消息,以及FActive是否為真:
  
  if?(Msg.Msg=WM_SYSCOMMAND)?and(FActive)?then
  
  兩樣都成立,執行裡面的代碼,代碼中有解釋,這裡只說兩個:
  
  SetWindowLong(Application.Handle,GWL_EXSTYLE?,WS_EX_TOOLWINDOW);
  
  設置了這個屬性後,窗口最小化就不會停在任務欄了,而是停在屏幕的某個位置,這個位置在哪裡呢,由
  
  placement.flags:=WPF_SETMINPOSITION;
  
  ?????placement.ptMinPosition.x:=1050;
  
  ?????placement.ptMinPosition.y:=800;
  
  ?????SetWindowPlacement(Application.Handle,@placement);
  
  決定,具體的看代碼,自己查幫助吧,這裡不多說
  
  ?
  
  而上說的設置SetWindowLong後,問題來了,窗口最小化的風格一變了,當你把Factive設為False,再最小化窗口,此時是沒有托盤圖標,但窗口還是最小化到屏幕的那個位置去了,我們看不到,又不能使其恢復(沒有托盤)。怎麼辦呢,
  
  原來還有一個GetWindowLong函數會返回當前風格的值,我們可以在控件的構造函數中這樣調用
  
  OldStyleEX:=GetWindowLong(Application.Handle,GWL_EXSTYLE);
  
  這時,OldStyleEX:就保存了窗口原來最小化的風格了,窗口最小化,調用SetWindowLong,設置了新的最小風格。而當我們觸發托盤事件,使窗體恢復大小時,我們在處理函數中調用
  
  SetWindowLong(Application.Handle,GWL_EXSTYLE?,OldStyleEX);
  
  這樣,窗口又回到了原來的風格,這時我們設Factive為False,則窗口就能正常最小化了。
  
  ?
  
  到控件被釋放時,我們一定要調用Application.UnhookMainWindow(AppMsgHook);來解除鉤子
  
  ?
  
  其實這裡也有一個不完善的地方,應該再設一個成員變量,確定設置托盤時,窗口是正常最小化,還是最小化到看不見。而我沒有這麼做,直接如果FActive為True,最小化會出現托盤圖標,並且窗口最小化到看不見。不過影響不大,有興趣的朋友看了之後可以幫我完善一下,也當做自己的練習嗎。
  
  ?
  
  三?托盤如果處理消息,上面說到,要設置托盤結構,一定要有一個窗口句柄,才能檢索托盤消息,那麼這個句柄是什麼呢,非可視組件沒有窗口句柄呀。
  
  ?
  
  如果你有看過TTimer的源碼,一定知道這一句代碼:
  
  FWindowHandle?:=?AllocateHWnd(WndProc);
  
  它創建一個看不見的窗口,返回他的句柄,並指定WndProc為窗口的消息處理過程
  
  我們何不效仿它呢。
  
  於是也定義一個成員句柄:
  
  FHandle:?HWnd;
  
  把該句柄賦給NOTIFYICONDATA的hWnd字段
  
  再定義一個消息處理過程:
  
  procedure?WndProc(var?Msg:?TMessage);
  
  再在組件構造函數中:
  
  FHandle?:=?AllocateHWnd(WndProc);
  
  如此之後,組件就可以截獲托盤的消息了,並在WndProc過程中作相應處理。這裡有必要對托盤的自定義消息做一個介紹:
  
  我們自定義了這個消息WM_TrayMsg,它的lParam與托盤的uID相同,wParam是鼠標在圖標上發生的事件消息,比如單擊,雙擊等。
  
  我們就要把這些消息轉化為事件,供給用戶處理,所以定義幾個事件調度函數:
  
  //以下為事件的調度函數
  
  ????procedure?DblClick;?dynamic;
  
  ????procedure?Click;?dynamic;
  
  ????procedure?MouseDown(Button:?TMouseButton;?Shift:?TShiftState;?X,?Y:?Integer);?dynamic;
  
  ????procedure?MouseUp(Button:?TMouseButton;?Shift:?TShiftState;?X,?Y:?Integer);?dynamic;
  
  procedure?MouseMove(Shift:?TShiftState;?X,?Y:?Integer);?dynamic;
  
  意思很明顯,不多說,
  
  當然也有幾個事件方法指針:
  
  FOnIconClick:?TNotifyEvent;
  
  FOnIconDblClick:?TNotifyEvent;
  
  FOnIconMouseMove:?TMouseMoveEvent;
  
  FOnIconMouseDown:?TMouseEvent;
  
  FOnIconMouseUp:?TMouseEvent;
  
  然後在WndProc中判斷消息,並調用相應的事件調度函數。看代碼吧,有解釋。
  
  ?
  
  好了,三個技術解決了,第四個呢,還是等代碼出來以後再加組件編輯器吧。以下是源代碼:
  
  ?
  
  unit?MyTray;
  
  ?
  
  interface
  
  ?
  
  uses
  
  ??Windows,?Messages,?SysUtils,?Classes,?Graphics,?Controls,
  
  ??Forms,?Dialogs,?ShellApi,?ExtCtrls,StdCtrls;
  
  ?
  
  const
  
  //自定義托盤消息
  
  ???WM_TrayMsg=WM_USER+10;
  
  ?
  
  type
  
  ?//恢復窗口的方式,左雙擊,右雙擊,左單擊,右雙擊
  
  ??TRMode=(LDbClick,RDbClick,LCLick,RClick);
  
  ?
  
  ??TMyTray=class(TComponent)
  
  ??private
  
  ??//私有成員
  
  ????FIcon:TIcon;???//圖標
  
  ????FDfIcon:THandle;?//應用程序的默認圖標
  
  ????FSetDfIcon:Boolean;?//是否用應用程序的圖標,如果為True,則Ficon為nil
  
  ????FIconData:?TNotifyIconData;??//托盤數據結構
  
  ????isMin:Boolean;//標識是否窗口最小化了
  
  ????FHandle:?HWnd;??//不可視建窗體句柄,用於處理托盤事件
  
  ????FActive:?Boolean;??//是否啟用托盤
  
  ????FHint:?string;??//托盤提示字符串
  
  ????FRMode:TRMode;?//恢復窗口的方式
  
  ????isClickIn:Boolean;//標識鼠標是否點在圖標上
  
  ????OldStyleEX:longInt;?//保存老的窗口風格
  
  ??//事件成員
  
  ????FOnIconClick:?TNotifyEvent;
  
  ????FOnIconDblClick:?TNotifyEvent;
  
  ????FOnIconMouseMove:?TMouseMoveEvent;
  
  ????FOnIconMouseDown:?TMouseEvent;
  
  ????FOnIconMouseUp:?TMouseEvent;
  
  ??//設置方法
  
  ????procedure?SetIcon(value:TIcon);
  
  ????procedure?SetDfIcon(value:boolean);
  
  ????procedure?SetActive(value:boolean);
  
  ????procedure?SetHint(value:string);
  
  ????procedure?SetRMode(value:TRMode);
  
  ??//私有方法
  
  ????procedure?SetTray(Way:DWord);??//設置托盤樣式,修改,刪除,增加
  
  ????function?GetActiveIcon:THandle;?//取得有用的圖標句柄
  
  ??protected
  
  ????//應用程序的消息鉤子,獲得主窗口的最小化消息
  
  ????function?AppMsgHook(var?Msg:TMessage):Boolean;
  
  ????procedure?WndProc(var?Msg:?TMessage);//不可視窗口的窗口過程
  
  ????//以下為事件的調度函數
  
  ????procedure?DblClick;?dynamic;
  
  ????procedure?Click;?dynamic;
  
  ????procedure?MouseDown(Button:?TMouseButton;?Shift:?TShiftState;?X,?Y:?Integer);?dynamic;
  
  ????procedure?MouseUp(Button:?TMouseButton;?Shift:?TShiftState;?X,?Y:?Integer);?dynamic;
  
  ????procedure?MouseMove(Shift:?TShiftState;?X,?Y:?Integer);?dynamic;
  
  ??public
  
  ?????constructor?Create(AOwner:TComponent);override;
  
  ?????destructor??Destroy;override;
  
  ??published
  
  ?????property?Active:Boolean?read?FActive?write?SetActive?default?False;
  
  ?????property?Icon:TIcon?read?FIcon?write?SetICon;
  
  ?????property?SetDfIconed:?boolean?read?FSetDfIcon?write?SetDfIcon?default?true;
  
  ?????property?Hint:String?read?FHint?write?SetHint;
  
  ?????property?RMode:TRmode?read?FRmode?write?SetRMode?default?LDbClick;
  
  ??//事件的方法指針
  
  ?????property?OnIconClick:?TNotifyEvent?read?FOnIconClick?write?FOnIconClick;
  
  ?????property?OnIconDblClick:?TNotifyEvent?read?FOnIconDblClick?write?FOnIconDblClick;
  
  ?????property?OnIconMouseMove:?TMouseMoveEvent?read?FOnIconMouseMove?write?FOnIconMouseMove;
  
  ?????property?OnIconMouseDown:?TMouseEvent?read?FOnIconMouseDown?write?FOnIconMouseDown;
  
  ?????property?OnIconMouseUp:?TMouseEvent?read?FOnIconMouseUp?write?FOnIconMouseUp;
  
  ??end;
  
  ?
  
  procedure?Register;
  
  ?
  
  implementation
  
  ?
  
  procedure?Register;
  
  begin
  
  ??RegisterComponents('Wind',?[TMyTray]);
  
  end;
  
  ?
  
  ///////////TmyTray////////////////////////////
  
  constructor?TMyTray.Create(AOwner:TComponent);
  
  begin
  
  ??inherited?Create(AOwner);
  
  ??//設置程序鉤子,指定AppMsgHook為處理函數,
  
  ??//則,應用程序的任何消息都將經過這個函數
  
  ???Application.HookMainWindow(AppMsgHook);
  
  ???FICon:=TICon.Create;
  
  ???//得到默認圖標的句柄,圖標為應用程序的圖標
  
  ???FDfIcon:=Application.Icon.Handle;
  
  ???FSetDfIcon:=True;
  
  ???FActive:=False;
  
  ???FRMode:=LDbClick;
  
  ???isMin:=False;
  
  ??//創建一個不可視窗口,並指定窗口過程,以處理托盤事件
  
  ????FHandle?:=?AllocateHWnd(WndProc);
  
  ??//保存窗體的老的風格,在恢復窗口的同時也恢復原來的窗口風格
  
  ????OldStyleEX:=GetWindowLong(Application.Handle,GWL_EXSTYLE);
  
  end;
  
  ?
  
  destructor?TMyTray.Destroy;
  
  begin
  
  ??Application.UnhookMainWindow(AppMsgHook);
  
  ??//對象釋放之前先消除托盤
  
  ????SetTray(NIM_DELETE);
  
  ??//釋放不可能窗口的句柄
  
  ??DeallocateHWnd(FHandle);
  
  ??FICon.Free;
  
  ??inherited?Destroy;
  
  end;
  
  //應用程序鉤子,可以截獲應用程序的所有消息
  
  function?TMyTray.AppMsgHook(var?Msg:TMessage):Boolean;
  
  var?placement:WINDOWPLACEMENT;
  
  begin
  
  ?Result:=False;
  
  ?//保證程序不會在設計時處理最小化消息
  
  ?if?not?(csDesigning?in?ComponentState)?then
  
  ?if?(Msg.Msg=WM_SYSCOMMAND)?and(FActive)?then
  
  ?begin
  
  ???if?msg.WParam=SC_MINIMIZE?Then
  
  ????begin
  
  ????//設置了這個屬性後,窗口最小化就不會停在任務欄了,而是停在屏幕,
  
  ????//位置由SetWindowPlacement來決定
  
  ?????ShowWindow(Application.Handle,SW_HIDE);
  
  ?????SetWindowLong(Application.Handle,GWL_EXSTYLE??????,WS_EX_TOOLWINDOW);
  
  ?????GetWindowPlacement(Application.Handle,@placement);
  
  ?????placement.flags:=WPF_SETMINPOSITION;
  
  ?????placement.ptMinPosition.x:=1050;
  
  ?????placement.ptMinPosition.y:=800;
  
  ?????SetWindowPlacement(Application.Handle,@placement);
  
  ?????SetTray(NIM_ADD?);
  
  ???end;
  
  ?end;
  
  end;
  
  ?
  
  procedure?TMyTray.SetIcon(Value:TIcon);
  
  begin
  
  ???FIcon.Assign(Value);
  
  ???FsetDfIcon:=False;?//有了自定義的圖標,則默認圖標自動設為False
  
  ???if?FIcon.Empty?then
  
  ????FsetDfIcon:=True;
  
  ???if?(isMin)and(Factive)?then
  
  ?????SetTray(NIM_MODIFY?);
  
  end;
  
  //設置是否為默認圖標,與FIcon為互相的變量,只能有其中一個
  
  procedure?TMyTray.SetDfIcon(Value:Boolean);
  
  begin
  
  ??if?FSetDfIcon<>Value?then
  
  ??begin
  
  ????FSetDfIcon:=Value;
  
  ????if?not?FSetDfIcon?then
  
  ????begin
  
  ??????if?FIcon.Empty?then?begin
  
  ????????FSetDfIcon:=True;
  
  ????????exit;
  
  ??????end;
  
  ????end
  
  ????else?begin
  
  ????????if?(IsMin)and(FActive)?then
  
  ?????????SetTray(NIM_MODIFY);
  
  ????end;
  
  ??end;
  
  end;
  
  ?
  
  procedure?TMyTray.SetActive(Value:Boolean);
  
  begin
  
  ??if?FActive<>Value?then
  
  ??begin
  
  ????FActive:=Value;
  
  ??end;
  
  end;
  
  ?
  
  procedure?TMyTray.SetHint(Value:String);
  
  begin
  
  ???if?FHint<>Value?then
  
  ???begin
  
  ?????FHInt:=Value;
  
  ?????if?(IsMin)and(FActive)?then
  
  ????????SetTray(NIM_MODIFY);
  
  ???end;
  
  end;
  
  ?
  
  procedure?TMyTray.SetRMode(Value:TRMode);
  
  begin
  
  ??if?FRmode<>Value?then
  
  ????FRmode:=Value;
  
  end;
  
  //設置托盤方式,顯示,修改,刪掉,重要方法
  
  procedure?TMyTray.SetTray(Way:DWord);
  
  begin
  
  ???FIconData.cbSize:=Sizeof(FIconData);
  
  ???FIconData.Wnd:=FHandle;
  
  ???FIConData.uID:=0;
  
  ???FIConData.uFlags:=NIF_ICON?or?NIF_MESSAGE?or?NIF_TIP;
  
  ???FIConData.uCallbackMessage:=WM_TrayMsg;
  
  ???FIConData.hIcon:=GetActiveIcon;
  
  ???StrLCopy(FIConData.szTip,Pchar(FHint),63);
  
  ???Shell_NotifyIcon(Way,@FIconData);
  
  end;
  
  //取得可用的圖標
  
  function?TMyTray.GetActiveIcon:THandle;
  
  begin
  
  ???if?not?FSetDfIcon?then
  
  ?????result:=FIcon.Handle
  
  ???else
  
  ?????result:=FDfIcon;
  
  end;
  
  //托盤消息的截獲,以調用相應的事件調度方法
  
  procedure?TMyTray.WndProc(var?Msg:?TMessage);
  
  var?p:TPoint;
  
  begin
  
  ??if?(Msg.Msg=WM_TrayMsg)and(FActive)?then
  
  ??begin
  
  ????case?Msg.LParam?of
  
  ??????WM_LBUTTONDBLCLK://左雙擊
  
  ??????begin
  
  ????????GetCursorPos(p);
  
  ????????DblClick;
  
  ????????MouseDown(mbLeft,?KeysToShiftState(TWMMouse(Msg).Keys)+[ssDouble],?P.X,?P.Y);
  
  ????????if?FRmode=LDbclick?then
  
  ????????begin
  
  ??????????ShowWindow(Application.Handle,SW_SHOW);
  
  ??????????//這裡很重要的一個就是恢復窗口風格,不然下次把Active設為True
  
  ??????????//最小化後,窗口依然會往左下角飛去,而托盤圖標卻看不見了.
  
  ??????????SetWindowLong(Application.Handle,GWL_EXSTYLE????,OldStyleEX);
  
  ??????????SendMessage(Application.Handle,WM_SYSCOMMAND,SC_RESTORE,0);
  
  ??????????SetTray(NIM_DELETE);
  
  ????????end;
  
  ??????end;
  
  ??????WM_RBUTTONDBLCLK://右雙擊
  
  ??????begin
  
  ????????GetCursorPos(P);
  
  ????????DblClick;
  
  ????????MouseDown(mbRight,?KeysToShiftState(TWMMouse(Msg).Keys)+[ssDouble],?P.X,?P.Y);
  
  ????????if?FRmode=RDbclick?then
  
  ????????begin
  
  ??????????ShowWindow(Application.Handle,SW_SHOW);
  
  ??????????SetWindowLong(Application.Handle,GWL_EXSTYLE????,OldStyleEX);
  
  ??????????SendMessage(Application.Handle,WM_SYSCOMMAND,SC_RESTORE,0);
  
  ??????????SetTray(NIM_DELETE?);
  
  ????????end;
  
  ??????end;
  
  ??????WM_MOUSEMOVE:?//鼠標移動
  
  ??????begin
  
  ????????GetCursorPos(P);
  
  ????????MouseMove(KeysToShiftState(TWMMouse(Msg).Keys),?P.X,?P.Y);
  
  ??????end;
  
  ??????WM_LBUTTONDOWN:?//左單擊下
  
  ??????begin
  
  ????????GetCursorPos(P);
  
  ????????IsClickIn:=True;
  
  ????????MouseDown(mbLeft,?KeysToShiftState(TWMMouse(Msg).Keys)?+?[ssLeft],?P.X,?P.Y);
  
  ??????end;
  
  ??????WM_LBUTTONUP:??//左單擊彈起
  
  ??????begin
  
  ????????GetCursorPos(P);
  
  ????????if?IsClickIn?then
  
  ????????begin
  
  ??????????IsClickIn:=False;
  
  ??????????Click;
  
  ??????????if?FRmode=LClick?then
  
  ??????????begin
  
  ????????????ShowWindow(Application.Handle,SW_SHOW);
  
  ????????????SetWindowLong(Application.Handle,GWL_EXSTYLE?,OldStyleEX);
  
  ????????????SendMessage(Application.Handle,WM_SYSCOMMAND,SC_RESTORE,0);
  
  ????????????SetTray(NIM_DELETE?);
  
  ??????????end;
  
  ????????end;
  
  ??????????MouseUp(mbLeft,?KeysToShiftState(TWMMouse(Msg).Keys)+?[ssLeft],?P.X,?P.Y);
  
  ??????end;
  
  ??????WM_RBUTTONDOWN:?//右單擊下
  
  ??????begin
  
  ????????GetCursorPos(P);
  
  ????????IsClickIn:=True;
  
  ????????MouseDown(mbRight,?KeysToShiftState(TWMMouse(Msg).Keys)?+?[ssRight],?P.X,?P.Y);
  
  ??????end;
  
  ??????WM_RBUTTONUP:?//右單擊彈起
  
  ??????begin
  
  ????????GetCursorPos(P);
  
  ????????if?IsClickIn?then
  
  ????????begin
  
  ??????????IsClickIn:=False;
  
  ??????????Click;
  
  ??????????if?FRmode=RClick?then
  
  ??????????begin
  
  ????????????ShowWindow(Application.Handle,SW_SHOW);
  
  ????????????SetWindowLong(Application.Handle,GWL_EXSTYLE?,OldStyleEX);
  
  ????????????SendMessage(Application.Handle,WM_SYSCOMMAND,SC_RESTORE,0);
  
  ????????????SetTray(NIM_DELETE?);
  
  ??????????end;
  
  ????????end;
  
  ????????MouseUp(mbRight,?KeysToShiftState(TWMMouse(Msg).Keys)+?[ssRight],?P.X,?P.Y);
  
  ???????end;
  
  ??????end;
  
  ??end
  
  ??else
  
  ?????Msg.Result?:=?DefWindowProc(FHandle,?Msg.Msg,?Msg.wParam,?Msg.lParam);
  
  end;
  
  //以下為幾個事件的調度函數,比較簡單.
  
  procedure?TMyTray.DblClick;
  
  begin
  
  ??if?Assigned(FOnIconDblClick)?then
  
  ????FOnIconDblClick(Self);
  
  end;
  
  ?
  
  procedure?TMyTray.Click;
  
  begin
  
  ??if?Assigned(FOnIconClick)?then
  
  ????FOnIconClick(Self);
  
  end;
  
  ?
  
  procedure?TMyTray.MouseDown(Button:?TMouseButton;?Shift:?TShiftState;?X,?Y:?Integer);
  
  begin
  
  ??if?Assigned(FOnIconMouseDown)?then
  
  ????FOnIconMouseDown(Self,?Button,?Shift,?X,?Y);
  
  end;
  
  ?
  
  procedure?TMyTray.MouseUp(Button:?TMouseButton;?Shift:?TShiftState;?X,?Y:?Integer);
  
  begin
  
  ??if?Assigned(FOnIconMouseUp)?then
  
  ????FOnIconMouseUp(Self,?Button,?Shift,?X,?Y);
  
  end;
  
  ?
  
  procedure?TMyTray.MouseMove(Shift:?TShiftState;?X,?Y:?Integer);
  
  begin
  
  ??if?Assigned(FOnIconMouseMove)?then
  
  ????FOnIconMouseMove(Self,?Shift,?X,?Y);
  
  end;
  
  ?
  
  end.
  
  ?
  
  組制作完畢,相信經過上面的講解,以及代碼的注釋,應該不難理解。接下來是什麼呢,給我的托盤控件來點效果,即在設計器中,當雙擊該組件,或右擊快捷菜單第一項時,會彈出一個About對話框,來說明我的托盤組件。
  
  這個就要用到組件編輯器啦?。幾本經典書中都有說及,比如Deplphi開發人員指南,我也是從那裡學來的,不過卻遇到了一些問題,折磨了幾天才解決。
  
  這裡不想詳細介紹,去看一下那些書,大概也就知道了,只略說一下。
  
  其原理就是實現一個繼承自TComponentEditor的子類TTrayIconEditor,並在其中覆蓋以下三個方法:
  
  function?GetVerbCount:?Integer;?override;
  
  function?GetVerb(Index:?Integer):?string;?override;
  
  procedure?ExecuteVerb(Index:?Integer);?override;
  
  可以精略理解為:
  
  GetVerbCount指定控件快捷菜單的項數
  
  GetVerb指定快捷菜單中的相關項的名字
  
  ExecuteVerb執行點擊快捷菜單項後的動作
  
  ?
  
  接著在Register方法中調用RegisterComponentEditor(TMyTray,TTrayIconEditor);
  
  第一個參數為組件類名,第二個為組件編輯器的類名。
  
  而上面的方法必須引用DesignIntf,DesignEditors。
  
  ?
  
  當我在我的組件單元這樣做之後出現問題了,編譯安裝沒有問題。我建立測試程序,並拉一個托盤組件,雙擊它,可以出現About對話框,右擊菜單第一項也沒有問題。可是當我運行測試程序時,卻出現了這樣的編譯錯誤:
  
  [Fatal?Error]?Unit1.pas(7):?File?not?found:?'DesignEditors.dcu'
  
  ?
  
  這讓我痛苦了好幾天,書上是這麼說的,應該沒有什麼錯誤呀。後來經過摸索,才找到了解決之道。
  
  解決的辦法就是將組件編輯器類放在另一個單元中,並在這個單元引用我的托盤組件單元。
  
  並安裝之。這才可以正常運行,這個編輯器單元如下:
  
  ?
  
  unit?AboutTray;
  
  ?
  
  interface
  
  ?
  
  uses
  
  ??SysUtils,Classes,DesignIntf,DesignEditors,Forms,
  
  ??MyTray;
  
  ?
  
  type
  
  ?TTrayIconEditor?=?class?(TComponentEditor)
  
  ????function?GetVerbCount:?Integer;?override;
  
  ????function?GetVerb(Index:?Integer):?string;?override;
  
  ????procedure?ExecuteVerb(Index:?Integer);?override;
  
  ??end;
  
  ?
  
  procedure?Register;
  
  ?
  
  implementation
  
  ?
  
  ///////TTrayIconEditor////////////////////////
  
  procedure?TTrayIconEditor.ExecuteVerb(index:integer);
  
  begin
  
  ?case?index?of
  
  ?0:?application.MessageBox('你好,這是風做的托盤組件!!','關於');
  
  ?end;
  
  end;
  
  ?
  
  function?TTrayIconEditor.GetVerb(index:integer):String;
  
  begin
  
  ??case?index?of
  
  ????0:Result:='About?MyTray';
  
  ??end;
  
  end;
  
  ?
  
  function?TTrayIconEditor.GetVerbCount:integer;
  
  begin
  
  ??Result:=1;
  
  end;
  
  ?
  
  ?
  
  procedure?Register;
  
  begin
  
  ??RegisterComponentEditor(TMyTray,TTrayIconEditor);
  
  end;
  
  ?
  
  end.
  
  ?
  
  至此,托盤組件完畢,拉下它放在窗體設計器中,雙擊,彈出對話框
  
  裡面內容為:“你好,這是風做的托盤組件!!”。哈哈,你成功啦
  
  ?
  
  ?
  
  做為組件制作的最後一個內容,我想用一個包來把我的所有組件單元包含起來,並放在我自己新建的一個面板中。
  
  這樣做之前,要把以前安裝下去的組件刪除。知道怎麼樣刪除,如果不知道,請看我在第一篇中說的。
  
  然後在打開所有的組件單元,把RegisterComponents(‘Samples',?[TCoolMemo]);裡面的
  
  Samples改為Wind。然後保存
  
  接著,在IDE中點File-》New-》Other…
  
  彈出來的New?Items對話框,選中New頁面,並選中其中的Package,
  
  這裡彈出一個新建的包編輯器。
  
  先在IDE中點File-》Save。將包編輯器保存。保存在組件的單元所在的文件夾中
  
  我的所有組件單元都放在Delphi7MyCom文件夾中。因此這個包當然也保存在這裡。
  
  ?
  
  然後,點包編輯器上邊的Add,將所有的組件單元加進去,當然也保括上面說的組件編輯器單元啦。
  
  加進去後,點包編輯器上邊的Compile,編譯完畢,再點Insall。
  
  成功,看看面板。所有以前做過的組件全在Wind面板中了。
  
  而這時候,我的任務也完畢了。
  
  ?
  
  ?
  
  結語
  
  ?
  
  這次的組件之旅終於走完了,也許有人會笑我淺薄,認為這麼簡單的東西,有必要拿出來麼。也許是比較簡單吧,但一定有人會需要的,相信我的文章會給他們幫助的。因為這些是我曾經學到的知道,遇到的問題並解決它。所以我個人覺得是很珍貴的。並且經過寫這幾篇,我把這些知識記得更牢了。這種利己利人的事,何樂而不為呀。
  
  在此,謝謝大家的閱讀,也許下次還有機會再見面,不過現在要說再見了。祝你們愉快。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved