這將是最後一個組件了,目標定為非可視化,事實上非可視化組件要比可視化組件難做,因為是從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面板中了。
而這時候,我的任務也完畢了。
?
?
結語
?
這次的組件之旅終於走完了,也許有人會笑我淺薄,認為這麼簡單的東西,有必要拿出來麼。也許是比較簡單吧,但一定有人會需要的,相信我的文章會給他們幫助的。因為這些是我曾經學到的知道,遇到的問題並解決它。所以我個人覺得是很珍貴的。並且經過寫這幾篇,我把這些知識記得更牢了。這種利己利人的事,何樂而不為呀。
在此,謝謝大家的閱讀,也許下次還有機會再見面,不過現在要說再見了。祝你們愉快。