`
javasogo
  • 浏览: 1778672 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

组件制作之五(托盘组件)

阅读更多

这将是最后一个组件了,目标定为非可视化,事实上非可视化组件要比可视化组件难做,因为是从TComponent继承而来,就没有了很多属性和事件。而这些都要我们从头来做过。

这个非可视化组件,我决定为托盘组件,其中用到的技术较多,我不如列一个表出来,然后再来讲解好一点。另外,可能篇幅会多一些,请耐心看。

用到的技术:

1作为核心功能,当然是托盘的应用啦。

2 托盘组件怎么样影响到主窗口最小化时隐藏

3 托盘如何处理消息

4 组件编辑器的用法

上面每一个技术都非常有趣,让我们一个个来看吧:

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

托盘,是系统壳编程的一个功能,相信我们也看过很多啦,大概知道它用起来是什么样子的。

那么它是如何实现的呢,

Windows定义了这样一个结构来存放托盘的信息:

typedef struct _NOTIFYICONDATA { // nid

DWORD cbSize;

HWND hWnd;

UINT uID;

UINT uFlags;

UINT uCallbackMessage;

HICON hIcon;

char szTip[64];

} NOTIFYICONDATA, *PNOTIFYICONDATA;

cbSizeNOTIFYICONDATA结构的尺寸,我们一般用Sizeof就可以了

hWnd一个窗口句柄,用于检索托盘消息的。然而我们的非可视组件并没有窗口呀,这就是技术列表第三条要讲的,这里从略

uID 一标识托盘图标的,我们可以随便指定一个数,但如果同时有不同的图标,则数应该不同

uFlagsNIF_ICONNIF_MESSAGENIF_TIP中的一个或多个,我们全用就可以了。

uCallbackMessage;托盘消息,是我们自定义的消息,这里我们定义为:

const

WM_TrayMsg=WM_USER+10;

hIcon托盘图标句柄

szTip这个是托盘提示,当托盘出现时,鼠标移到哪里,就会出现该提示。

Delphi将这个结构重定义为TNotifyIconData,我们照这个来用就行了

我们应用托盘要用到API函数Shell_NotifyIcon,其中有两个参数,第一个为

NIM_ADDNIM_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

这样的意图是很明显的,因为当设计时的主窗其实是DelphiIDE,如果让他处理该消息,其实是处理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);

这样,窗口又回到了原来的风格,这时我们设FactiveFalse,则窗口就能正常最小化了。

到控件被释放时,我们一定要调用Application.UnhookMainWindow(AppMsgHook);来解除钩子

其实这里也有一个不完善的地方,应该再设一个成员变量,确定设置托盘时,窗口是正常最小化,还是最小化到看不见。而我没有这么做,直接如果FActiveTrue,最小化会出现托盘图标,并且窗口最小化到看不见。不过影响不大,有兴趣的朋友看了之后可以帮我完善一下,也当做自己的练习吗。

托盘如果处理消息,上面说到,要设置托盘结构,一定要有一个窗口句柄,才能检索托盘消息,那么这个句柄是什么呢,非可视组件没有窗口句柄呀。

如果你有看过TTimer的源码,一定知道这一句代码:

FWindowHandle := AllocateHWnd(WndProc);

它创建一个看不见的窗口,返回他的句柄,并指定WndProc为窗口的消息处理过程

我们何不效仿它呢。

于是也定义一个成员句柄:

FHandle: HWnd;

把该句柄赋给NOTIFYICONDATAhWnd字段

再定义一个消息处理过程:

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,则Ficonnil

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;

分享到:
评论

相关推荐

    电信设备-摄影机镜头模块制作用托盘组件.zip

    电信设备-摄影机镜头模块制作用托盘组件.zip

    delphi 开发经验技巧宝典源码

    0002 如何定制组件面板 2 0003 如何定制代码编辑器 3 0004 保存自定义开发环境桌面 4 1.2 组件安装 4 0005 安装ActiveX组件 4 0006 安装不同类型的第三方组件 5 0007 在Delphi中加载QReport报表组件 7...

    delphi 开发经验技巧宝典源码06

    0002 如何定制组件面板 2 0003 如何定制代码编辑器 3 0004 保存自定义开发环境桌面 4 1.2 组件安装 4 0005 安装ActiveX组件 4 0006 安装不同类型的第三方组件 5 0007 在Delphi中加载QReport报表组件 7...

    C++Builder精彩编程实例集锦的源代码(1,2,3部分).rar

    实例031 如何使用组件创建托盘程序 实例032 如何使用动画组件播放动画 实例033 如何在状态栏上加载进度条 实例034 如何在编辑框内显示图像 实例035 如何在列表框中增加水平滚动条 实例036 如何在列表框中同时...

    C++Builder精彩编程实例集锦的源代码前3部分.rar

    实例031 如何使用组件创建托盘程序 实例032 如何使用动画组件播放动画 实例033 如何在状态栏上加载进度条 实例034 如何在编辑框内显示图像 实例035 如何在列表框中增加水平滚动条 实例036 如何在列表框中同时...

    C++Builder精彩编程实例集锦的源代码后3部分.rar

    实例031 如何使用组件创建托盘程序 实例032 如何使用动画组件播放动画 实例033 如何在状态栏上加载进度条 实例034 如何在编辑框内显示图像 实例035 如何在列表框中增加水平滚动条 实例036 如何在列表框中同时...

    第一部分 界面设计

    实例031 如何使用组件创建托盘程序 实例032 如何使用动画组件播放动画 实例033 如何在状态栏上加载进度条 实例034 如何在编辑框内显示图像 实例035 如何在列表框中增加水平滚动条 实例036 如何在列表框中同时...

    C#.net_经典编程例子400个

    142 3.6 Process组件 143 实例102 使用Process组件访问本地进程 143 3.7 Timer组件 145 实例103 使用Timer组件制作计时器 145 实例104 使用Timer组件显示当前系统时间 150 实例105 ...

    Delphi7编程100例

    用DBGrid组件制作下拉列表形式的提示框 利用书签处理DBGrid中的多个记录 将ComboBox中的内容直接拖放到DBGrid里 如何在DBGrid里放置图标 DBGrid控件精彩组合 DBChart图表控件的使用 在StringGrid组件中...

    Delphi编程100例

    用DBGrid组件制作下拉列表形式的提示框 利用书签处理DBGrid中的多个记录 将ComboBox中的内容直接拖放到DBGrid里 如何在DBGrid里放置图标 DBGrid控件精彩组合 DBChart图表控件的使用 在StringGrid组件中显示查询结果 ...

    《Delphi7编程100例》代码

    ToolBar工具栏控件的使用动态建立主菜单选项窗口界面的动态分隔条动态设置...组件制作下拉列表形式的提示框利用书签处理DBGrid中的多个记录将ComboBox中的内容直接拖放到DBGrid里如何在DBGrid里放置图标DBGrid控件精彩...

    C#程序开发范例宝典(第2版).part13

    实例113 使用Timer组件制作左右飘动的窗体 166 实例114 使用Timer组件实现奥运倒计时 167 3.8 ServiceController组件 169 实例115 使用ServiceController组件控制计算机的服务 169 3.9 ImageList组件 171 实例...

Global site tag (gtag.js) - Google Analytics