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

来源:互联网 发布:ubuntu 16.04 qq2017 编辑:程序博客网 时间:2024/05/18 20:47
%D%A%D%A%D%A

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

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

用到的技术:

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

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

3 托盘如何处理消息

4 组件编辑器的用法

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

%26nbsp;

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

那么它是如何实现的呢,

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

typedef struct _NOTIFYICONDATA { // nid%26nbsp;

DWORD cbSize;

%26nbsp;%26nbsp;%26nbsp; HWND hWnd;

%26nbsp;%26nbsp;%26nbsp; UINT uID;

%26nbsp;%26nbsp;%26nbsp; UINT uFlags;

%26nbsp;%26nbsp;%26nbsp; UINT uCallbackMessage;

%26nbsp;%26nbsp;%26nbsp; HICON hIcon;

%26nbsp;%26nbsp;%26nbsp; char szTip[64];

} NOTIFYICONDATA, *PNOTIFYICONDATA;

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

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

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

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

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

%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp; const

%26nbsp;%26nbsp; %26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp;WM_TrayMsg=WM_USER+10;

hIcon托盘图标句柄

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

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

%26nbsp;

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

NIM_ADDNIM_DELETE%26nbsp; NIM_MODIFY中的一个,分别表示添加托盘(图标出现)

修改托盘(比如图标,提示),删除(图标消失)第二个参数是NOTIFYICONDATA的指针

嗯,托盘应该差不多了。

%26nbsp;

这个组件能够决定主窗体最小化时,是否是正常最小化并没有托盘图标。还是最小化到屏幕之外,使我们看不见,且托盘区出现了图标。这里有一个成员为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方法啦,最小化消息也不例外,则我们可以在里面截获这个消息,并做一些操作:

%26nbsp;

做什么操作呢,先判断组件是否为设计时,如果是,不进行操作,如果不是进行下一步

if not (csDesigning in ComponentState) then

这样的意图是很明显的,因为当设计时的主窗其实是DelphiIDE,如果让他处理该消息,其实是处理IDE的最小化消息,这时如果你最小化IDE,就会出现托盘啦。所以不能。

%26nbsp;

下一步是是否截获了最小化消息,以及FActive是否为真:

if (Msg.Msg=WM_SYSCOMMAND) and(FActive) then

两样都成立,执行里面的代码,代码中有解释,这里只说两个:

SetWindowLong(Application.Handle,GWL_EXSTYLE ,WS_EX_TOOLWINDOW);

设置了这个属性后,窗口最小化就不会停在任务栏了,而是停在屏幕的某个位置,这个位置在哪里呢,由

placement.flags:=WPF_SETMINPOSITION;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; placement.ptMinPosition.x:=1050;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; placement.ptMinPosition.y:=800;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; SetWindowPlacement(Application.Handle,@placement);

决定,具体的看代码,自己查帮助吧,这里不多说

%26nbsp;

而上说的设置SetWindowLong后,问题来了,窗口最小化的风格一变了,当你把Factive设为False,再最小化窗口,此时是没有托盘图标,但窗口还是最小化到屏幕的那个位置去了,我们看不到,又不能使其恢复(没有托盘)。怎么办呢,

原来还有一个GetWindowLong函数会返回当前风格的值,我们可以在控件的构造函数中这样调用

OldStyleEX:=GetWindowLong(Application.Handle,GWL_EXSTYLE);

这时,OldStyleEX:就保存了窗口原来最小化的风格了,窗口最小化,调用SetWindowLong,设置了新的最小风格。而当我们触发托盘事件,使窗体恢复大小时,我们在处理函数中调用

SetWindowLong(Application.Handle,GWL_EXSTYLE ,OldStyleEX);

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

%26nbsp;

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

%26nbsp;

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

%26nbsp;

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

%26nbsp;

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

FWindowHandle := AllocateHWnd(WndProc);

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

我们何不效仿它呢。

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

FHandle: HWnd;

把该句柄赋给NOTIFYICONDATAhWnd字段

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

procedure WndProc(var Msg: TMessage);

再在组件构造函数中:

FHandle := AllocateHWnd(WndProc);

如此之后,组件就可以截获托盘的消息了,并在WndProc过程中作相应处理。这里有必要对托盘的自定义消息做一个介绍:

我们自定义了这个消息WM_TrayMsg,它的lParam与托盘的uID相同,wParam是鼠标在图标上发生的事件消息,比如单击,双击等。

我们就要把这些消息转化为事件,供给用户处理,所以定义几个事件调度函数:

//以下为事件的调度函数

%26nbsp;%26nbsp;%26nbsp; procedure DblClick; dynamic;

%26nbsp;%26nbsp;%26nbsp; procedure Click; dynamic;

%26nbsp;%26nbsp;%26nbsp; procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); dynamic;

%26nbsp;%26nbsp;%26nbsp; 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中判断消息,并调用相应的事件调度函数。看代码吧,有解释。

%26nbsp;

好了,三个技术解决了,第四个呢,还是等代码出来以后再加组件编辑器吧。以下是源代码:

%26nbsp;

unit MyTray;

%26nbsp;

interface

%26nbsp;

uses

%26nbsp; Windows, Messages, SysUtils, Classes, Graphics, Controls,

%26nbsp; Forms, Dialogs, ShellApi, ExtCtrls,StdCtrls;

%26nbsp;

const

//自定义托盘消息

%26nbsp;%26nbsp; WM_TrayMsg=WM_USER+10;

%26nbsp;

type

%26nbsp;//恢复窗口的方式,左双击,右双击,左单击,右双击

%26nbsp; TRMode=(LDbClick,RDbClick,LCLick,RClick);

%26nbsp;

%26nbsp; TMyTray=class(TComponent)

%26nbsp; private

%26nbsp; //私有成员

%26nbsp;%26nbsp;%26nbsp; FIcon:TIcon;%26nbsp;%26nbsp; //图标

%26nbsp;%26nbsp;%26nbsp; FDfIcon:THandle; //应用程序的默认图标

%26nbsp;%26nbsp;%26nbsp; FSetDfIcon:Boolean; //是否用应用程序的图标,如果为True,则Ficonnil

%26nbsp;%26nbsp;%26nbsp; FIconData: TNotifyIconData;%26nbsp; //托盘数据结构

%26nbsp;%26nbsp;%26nbsp; isMin:Boolean;//标识是否窗口最小化了

%26nbsp;%26nbsp;%26nbsp; FHandle: HWnd;%26nbsp; //不可视建窗体句柄,用于处理托盘事件

%26nbsp;%26nbsp;%26nbsp; FActive: Boolean;%26nbsp; //是否启用托盘

%26nbsp;%26nbsp;%26nbsp; FHint: string;%26nbsp; //托盘提示字符串

%26nbsp;%26nbsp;%26nbsp; FRMode:TRMode; //恢复窗口的方式

%26nbsp;%26nbsp;%26nbsp; isClickIn:Boolean;//标识鼠标是否点在图标上

%26nbsp;%26nbsp;%26nbsp; OldStyleEX:longInt; //保存老的窗口风格

%26nbsp; //事件成员

%26nbsp;%26nbsp;%26nbsp; FOnIconClick: TNotifyEvent;

%26nbsp;%26nbsp;%26nbsp; FOnIconDblClick: TNotifyEvent;

%26nbsp;%26nbsp;%26nbsp; FOnIconMouseMove: TMouseMoveEvent;

%26nbsp;%26nbsp;%26nbsp; FOnIconMouseDown: TMouseEvent;

%26nbsp;%26nbsp;%26nbsp; FOnIconMouseUp: TMouseEvent;

%26nbsp; //设置方法

%26nbsp;%26nbsp;%26nbsp; procedure SetIcon(value:TIcon);

%26nbsp;%26nbsp;%26nbsp; procedure SetDfIcon(value:boolean);

%26nbsp;%26nbsp;%26nbsp; procedure SetActive(value:boolean);

%26nbsp;%26nbsp;%26nbsp; procedure SetHint(value:string);

%26nbsp;%26nbsp;%26nbsp; procedure SetRMode(value:TRMode);

%26nbsp; //私有方法

%26nbsp;%26nbsp;%26nbsp; procedure SetTray(Way:DWORD);%26nbsp; //设置托盘样式,修改,删除,增加

%26nbsp;%26nbsp;%26nbsp; function GetActiveIcon:THandle; //取得有用的图标句柄

%26nbsp; protected

%26nbsp;%26nbsp;%26nbsp; //应用程序的消息钩子,获得主窗口的最小化消息

%26nbsp;%26nbsp;%26nbsp; function AppMsgHook(var Msg:TMessage):Boolean;

%26nbsp;%26nbsp;%26nbsp; procedure WndProc(var Msg: TMessage);//不可视窗口的窗口过程

%26nbsp;%26nbsp;%26nbsp; //以下为事件的调度函数

%26nbsp;%26nbsp;%26nbsp; procedure DblClick; dynamic;

%26nbsp;%26nbsp;%26nbsp; procedure Click; dynamic;

%26nbsp;%26nbsp;%26nbsp; procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); dynamic;

%26nbsp;%26nbsp;%26nbsp; procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); dynamic;

%26nbsp;%26nbsp;%26nbsp; procedure MouseMove(Shift: TShiftState; X, Y: Integer); dynamic;

%26nbsp; public

%26nbsp;%26nbsp;%26nbsp;%26nbsp; constructor Create(AOwner:TComponent);override;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; destructor%26nbsp; Destroy;override;

%26nbsp; published

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property Active:Boolean read FActive write SetActive default False;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property Icon:TIcon read FIcon write SetICon;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property SetDfIconed: boolean read FSetDfIcon write SetDfIcon default true;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property Hint:String read FHint write SetHint;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property RMode:TRmode read FRmode write SetRMode default LDbClick;

%26nbsp; //事件的方法指针

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property OnIconClick: TNotifyEvent read FOnIconClick write FOnIconClick;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property OnIconDblClick: TNotifyEvent read FOnIconDblClick write FOnIconDblClick;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property OnIconMouseMove: TMouseMoveEvent read FOnIconMouseMove write FOnIconMouseMove;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property OnIconMouseDown: TMouseEvent read FOnIconMouseDown write FOnIconMouseDown;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; property OnIconMouseUp: TMouseEvent read FOnIconMouseUp write FOnIconMouseUp;

%26nbsp; end;

%26nbsp;

procedure Register;

%26nbsp;

implementation

%26nbsp;

procedure Register;

begin

%26nbsp; RegisterComponents('Wind', [TMyTray]);

end;

%26nbsp;

///////////TmyTray////////////////////////////

constructor TMyTray.Create(AOwner:TComponent);

begin

%26nbsp; inherited Create(AOwner);

%26nbsp; //设置程序钩子,指定AppMsgHook为处理函数,

%26nbsp; //,应用程序的任何消息都将经过这个函数

%26nbsp; %26nbsp;Application.HookMainWindow(AppMsgHook);

%26nbsp;%26nbsp; FICon:=TICon.Create;

%26nbsp;%26nbsp; //得到默认图标的句柄,图标为应用程序的图标

%26nbsp;%26nbsp; FDfIcon:=Application.Icon.Handle;

%26nbsp;%26nbsp; FSetDfIcon:=True;

%26nbsp;%26nbsp; FActive:=False;

%26nbsp;%26nbsp; FRMode:=LDbClick;

%26nbsp;%26nbsp; isMin:=False;

%26nbsp; //创建一个不可视窗口,并指定窗口过程,以处理托盘事件

%26nbsp;%26nbsp;%26nbsp; FHandle := AllocateHWnd(WndProc);

%26nbsp; //保存窗体的老的风格,在恢复窗口的同时也恢复原来的窗口风格

%26nbsp;%26nbsp;%26nbsp; OldStyleEX:=GetWindowLong(Application.Handle,GWL_EXSTYLE);

end;

%26nbsp;

destructor TMyTray.Destroy;

begin

%26nbsp; Application.UnhookMainWindow(AppMsgHook);

%26nbsp; //对象释放之前先消除托盘

%26nbsp;%26nbsp;%26nbsp; SetTray(NIM_DELETE);

%26nbsp; //释放不可能窗口的句柄

%26nbsp; DeallocateHWnd(FHandle);

%26nbsp; FICon.Free;

%26nbsp; inherited Destroy;

end;

//应用程序钩子,可以截获应用程序的所有消息

function TMyTray.AppMsgHook(var Msg:TMessage):Boolean;

var placement:WINDOWPLACEMENT;

begin

%26nbsp;Result:=False;

%26nbsp;//保证程序不会在设计时处理最小化消息

%26nbsp;if not (csDesigning in ComponentState) then

%26nbsp;if (Msg.Msg=WM_SYSCOMMAND) and(FActive) then

%26nbsp;begin

%26nbsp;%26nbsp; if msg.WParam=SC_MINIMIZE Then

%26nbsp;%26nbsp;%26nbsp; begin

%26nbsp;%26nbsp;%26nbsp; //设置了这个属性后,窗口最小化就不会停在任务栏了,而是停在屏幕,

%26nbsp;%26nbsp;%26nbsp; //位置由SetWindowPlacement来决定

%26nbsp;%26nbsp;%26nbsp;%26nbsp; ShowWindow(Application.Handle,SW_HIDE);

%26nbsp;%26nbsp;%26nbsp;%26nbsp; SetWindowLong(Application.Handle,GWL_EXSTYLE%26nbsp;%26nbsp;%26nbsp;%26nbsp;%26nbsp; ,WS_EX_TOOLWINDOW);

%26nbsp;%26nbsp;%26nbsp;%26nbsp; GetWindowPlacement(Application.Handle,@placement);

%26nbsp;%26nbsp;%26nbsp;%26nbsp; placement.flags:=WPF_SETMINPOSITION;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; placement.ptMinPosition.x:=1050;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; placement.ptMinPosition.y:=800;

%26nbsp;%26nbsp;%26nbsp;%26nbsp; SetWindowPlacement(Application.Handle,@placement);

%26nbsp;%26nbsp;%26nbsp;%26nbsp; SetTray(NIM_ADD );

%26nbsp;%26nbsp; end;

%26nbsp;end;

end;

%26nbsp;

procedure TMyTray.SetIcon(Value:TIcon);

begin

%26nbsp;%26nbsp; FIcon.Assign(Value);

  • 组件制作之五(托盘组件)%A
  • 组件制作之五(托盘组件)
  • 组件制作之五(托盘组件)
  • 组件制作之五(托盘组件)
  • 组件制作五
  • 组件制作之四(定制外观)%A
  • 组件制作之三(图形控件)
  • 组件制作之四(定制外观)
  • 组件制作之三(图形控件)
  • 组件制作之四(定制外观)
  • 组件制作之四(定制外观)
  • RN之React组件通信(五)
  • 组件制作之二(一个简单组件的制作过程)
  • 组件制作之二(一个简单组件的制作过程)
  • CUDA学习之五(通用运行时组件)
  • Hibernate征途(五)之继承映射和组件映射
  • Android学习笔记之----初识四大(五大)组件
  • UI组件之TextView及其子类(五)计时器Chronometer
  • 我为什么没有学好java?
  • 项目经理与决策
  • 如何制作 Windows XP 的 USB 启动盘
  • 有关单片机串口的几个小招数但愿你能用得上
  • 中文参考手册---21怎样对比MySQL与其他
  • 组件制作之五(托盘组件)%A
  • Delphi技巧-编写Pascal代码
  • 用Delphi标准控件实现Access数据库的导入导出
  • 九种感觉
  • WML
  • 中文参考手册---20MySQL客户工具和API
  • Java中的transient,volatile和strictfp关键字
  • 中日对照OFFICE软件用语词汇汇总
  • 语言参考
  • 原创粉丝点击