消息问题 (10分)

  • 主题发起人 主题发起人 嫩手
  • 开始时间 开始时间

嫩手

Unregistered / Unconfirmed
GUEST, unregistred user!
移动鼠标怎么判断鼠标进入窗体和离开窗体?
CM_Mouse???????????
 
CM_MouseEnter,CM_MouseLeave
 
DELPHI预定义的两个消息:
CM_MOUSEENTER 和 CM_MOUSELEAVE。 Below 已经提供了部分使用它们的代码,
我试了,对TSPEEDBUTTON有效,相信对其它也有效。
unit SomeForm;
interface
type
TSomeForm = class(TForm)
private
procedure CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
end;
implementation
procedure TSomeForm.CMMouseEnter(var Msg: TMessage);
var
anObject :TObject;
begin
{ anObject is the control over which the mouse is right now }

anObject := TObject(Msg.lParam);
if anObject nil then begin
{ First, you must find WHICH is the control under the mouse cursor, }
{ then, determine what action to do, etc... }
end;
end;
procedure TSomeForm.CMMouseLeave(var Msg: TMessage);
begin
{ anObject is the control which the mouse has just gone out of }
anObject := TObject(Msg.lParam);
if anObject nil then begin
{ First, you must find WHICH is the control }
{ the mouse cursor has just left, }
{ then, determine what action to do, etc... }
end;
end;
end.
 
怎么没反应?我应用的 Form,但Form一点儿反应也没有
 
c++ builder里四种处理“鼠标离开窗体”事件的方法 hhdsq(原作)

关键字 鼠标,窗体,消息


在处理窗体消息的时候,我想不少人都会碰到鼠标离开窗体的消息(下面称之为MouseLeave)。在C++ Builder里,并没有直接提供处理这种消息的方法,需要我们自己动手来做。通过参考一些资料,我发现在C++ Builder里面处理MouseLeave,不外乎以下四种方法,现写出来供大家参考。如果有什么不对之处,请指正。

(一)、笨拙的Timer

每每提到捕捉鼠标离开窗体的消息的时候,也许有人就会马上想到用Timer来处理。不错,这种方法很简单,也确实有效。只须在Timer的OnTimer事件中判断鼠标所处位置的坐标是否在窗体内就可以了,详细代码如下:

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
POINT pt;
GetCursorPos(&pt); //得到鼠标的坐标
RECT rect;
GetWindowRect(Handle,&rect); //得到窗体的矩形范围
if(!PtInRect(rect,pt)) //判断鼠标的坐标是否在窗体的矩形范围内

Caption="out";
else
Caption="in";
}

为什么我要说是笨拙的Timer呢?原因有二:其一、OnTimer是优先级别比较低的消息,从严格意义上讲,上面这种做法并不精准。如果系统正在处理一大堆级别比较高的消息,那我们就无法及时获得MouseLeave消息。其二、Timer是比较宝贵的系统资源,用在MouseLeave上面似乎有些浪费了,因为我们还有更好的方法来做同样的事情。

(二)、霸道的SetCapture()

SetCapture()可以让指定的窗体捕获所有鼠标消息,当然也包括MouseLeave了:

void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
Caption="in";
SetCapture(Handle);
TPoint pt(X,Y);
TRect rect;
rect=GetClientRect();
if (!PtInRect(rect,pt))
{
ReleaseCapture();
Caption="out";
}
}

不过这种方法太过于霸道了,因为SetCapture()将所有的鼠标消息据为己有。虽然在捕获了MouseLeave以后已经ReleaseCapture了,但是在捕获过程中,你却无法对其他的鼠标消息做出反应。不信?你不妨在窗体在多放一个Button控件,再运行点点看?:)

(三)、受限的TrackMouseEvent()

MSDN上面说,TrackMouseEvent()可以让指定的窗体接受WM_MOUSELEAVE消息。但是在接受消息以后如果还要继续接受WM_MOUSELEAVE消息,必须重新调用TrackMouseEvent():

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
MouseTrack=false; //TForm1的私有变量,检测鼠标是否已经被Track
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WndProc(TMessage& Message) //重载WndProc
{
if (Message.Msg==WM_MOUSELEAVE) //在这里捕获WM_MOUSELEAVE消息
{
Caption="out";
MouseTrack=false; //鼠标Track已经完成
}
TForm::WndProc(Message);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
Caption="in";
if (!MouseTrack)
{
TRACKMOUSEEVENT tt;
tt.cbSize=sizeof(tt);
tt.dwFlags=TME_LEAVE;
tt.hwndTrack=Handle;
TrackMouseEvent(&tt);
MouseTrack=true; //开始鼠标Track
}
}
//---------------------------------------------------------------------------

这种方法很好用,唯一的缺点是可能不支持Win98(具体支不支持我也没有做过实验,哪位兄弟有Win98的帮我测试一下)。在Win2000的环境下,我推荐用这种方法,:)

(四)、未知的CM_MOUSELEAVE

在CSDN论坛里经常看到有人说可以通过捕获CM_MOUSELEAVE消息来达到同样的效果。不过根据我的测试,CM_MOUSELEAVE在控件上面工作得很好,可以用来捕获鼠标离开控件的消息。但用在窗体上似乎就不灵验了,可能我自己没有做对吧。如果有哪位大虾知道该怎么用,请告知小弟一声,我将感激不尽。

以上代码均在Win2k Professional+bcb6.0环境中编译成功

--------------------------------------
还有一种方法是用Mouse Hook,效果相当好,只是有点沙鸡用牛刀的感觉。
 
我对C很菜,能翻译成Delphi吗?
 
在不造成其它软件使用的情况下.建议使用三楼的第<一>种方法与安装鼠标勾子的方法.
假如你对SetWindowsHookEx(WH_MOUSE_LL,...), CallNextHookEx(), UnhookWindowsHookEx()
勾子函数使用自如.这种方法保证你在Windows下百发百种.
 
to bini:
我试过三楼的办法,但是没有反应?怎么回事?
 
用Timer定不要乱动程序了.因为当前窗口..,
我还是改口吧.用勾子.其实勾子很简单.要吃饭去了.
不然公司这一万多个人谁知道...吃在后面....
 
procedure CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
这方法怎么才能让它能用了?我很想用这个,好像这个比较方便吧,谁能给我个成功通过
的例子?x-delphi@163.com
 
在 Form 的 OnMouseMove 事件中判断鼠标是否在客户区,如:

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
P : TPoint;
begin
P.X := X;
P.Y := Y;
if PtInRect (ClientRect,P) then
MouseCapture := True
else
MouseCapture := False;

if MouseCapture then Color:=clBtnFace
else Color:=clRed;
end;
 
要回家过年了。做事需要举一反三。
看看下面的代码,根据无标题移动窗体的一段代码改写的,不过如果移动太快不行。


private
{ Private declarations }
procedure wmnchittest(var msg:Twmnchittest);message wm_nchittest;

procedure TForm1.wmnchittest(var msg: Twmnchittest);
begin
inherited;
if (msg.Result = htcaption) or (msg.Result = htclient) then
begin
caption := 'in';
end
else
begin
caption := 'out';
end;
end;
 
多人接受答案了。
 
后退
顶部