登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> 程序员学前班[不再更新,只读] >> 主题: WM_NCHITTEST 消息[判断鼠标在窗口的位置][欺骗Windows][指定自绘标题栏的位置]     [回主站]     [分站链接]
标题
WM_NCHITTEST 消息[判断鼠标在窗口的位置][欺骗Windows][指定自绘标题栏的位置]
clq
浏览(0) + 2010-07-20 11:07:38 发表 编辑

关键字:

WM_NCHITTEST 消息[判断鼠标在窗口的位置][欺骗Windows][指定自绘标题栏的位置]

WM_NCHITTEST 消息的作用真是大啊。

http://www.xuedelphi.cn/wenzhang/yyjc/2009/01/200901092984.htm
--------------------------------------------------
使用 WM_NCHITTEST 消息判断鼠标所在窗口的部位

本例效果图:



WM_NCHITTEST 消息返回后, 消息的 Result 参数表示了鼠标所在窗口的部位.

窗体设计步骤: 新建工程后, 随便添加一个菜单; 设置窗体的 AutoScroll 属性为 True, 并添加一个 Panel 放在合适的位置, 以让窗口出现滚动条.


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, Menus;

type
TForm1 = class(TForm)
Panel1: TPanel;
MainMenu1: TMainMenu;
mnuFile: TMenuItem;
private
procedure MyMsg(var msg: TWMNCHitTest); message WM_NCHITTEST;
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

const
arr: array[-2..21] of string = (
'HTERROR',
'HTTRANSPARENT',
'HTNOWHERE',
'HTCLIENT - 客户区',
'HTCAPTION - 标题',
'HTSYSMENU - 系统菜单',
'HTGROWBOX',
'HTMENU - 菜单',
'HTHSCROLL - 水平滚动条',
'HTVSCROLL - 垂直滚动条',
'HTMINBUTTON - 最小化按钮',
'HTMAXBUTTON - 最大化按钮',
'HTLEFT - 左边界',
'HTRIG - 右边界',
'HTTOP - 上边界',
'HTTOPLEFT - 左上角',
'HTTOPRIG - 右上角',
'HTBOTTOM - 下边界',
'HTBOTTOMLEFT - 左下角',
'HTBOTTOMRIG - 右下角',
'HTBORDER',
'HTOBJECT',
'HTCLOSE - 关闭按钮',
'HTHELP');

procedure TForm1.MyMsg(var msg: TWMNCHitTest);
var
i: Integer;
begin
Inherited;
i := msg.Result;
Text := Format('%d: %s', [i, arr[i]]);
end;

end.



//另附 WM_NCHITTEST 消息中 Result 参数的所有可能值的列表:
HTERROR = -2;
HTTRANSPARENT = -1;
HTNOWHERE = 0;
HTCLIENT = 1;
HTCAPTION = 2;
HTSYSMENU = 3;
HTGROWBOX = 4;
HTSIZE = HTGROWBOX;
HTMENU = 5;
HTHSCROLL = 6;
HTVSCROLL = 7;
HTMINBUTTON = 8;
HTMAXBUTTON = 9;
HTLEFT = 10;
HTRIGHT = 11;
HTTOP = 12;
HTTOPLEFT = 13;
HTTOPRIGHT = 14;
HTBOTTOM = 15;
HTBOTTOMLEFT = $10;
HTBOTTOMRIGHT = 17;
HTBORDER = 18;
HTREDUCE = HTMINBUTTON;
HTZOOM = HTMAXBUTTON;
HTSIZEFIRST = HTLEFT;
HTSIZELAST = HTBOTTOMRIGHT;
HTOBJECT = 19;
HTCLOSE = 20;
HTHELP = 21;


clq
2010-7-20 11:14:00 发表 编辑

事实上如果你改变消息的返回值,就能欺骗 windows 让 ta 为你做不少的工作。我用来改变自绘标题栏按钮的位置:


LRESULT CxxDlg::OnNcHitTest(CPoint point)
{
    // TODO: Add your message handler code here and/or call default

    //DrawCaption();//闪烁太厉害//可以考虑只画按钮部分

    //return TRUE;
    //CDialog::OnNcHitTest(point);return FALSE;

    DrawCaption();

    LRESULT r = CDialog::OnNcHitTest(point);

    //在这里可以修改当前的 point [其实就是鼠标点击的位置] 为什么位置
    /*
    'HTERROR',
    'HTTRANSPARENT',
    'HTNOWHERE',
    'HTCLIENT - 客户区',
    'HTCAPTION - 标题',
    'HTSYSMENU - 系统菜单',
    'HTGROWBOX',
    'HTMENU - 菜单',
    'HTHSCROLL - 水平滚动条',
    'HTVSCROLL - 垂直滚动条',
    'HTMINBUTTON - 最小化按钮',
    'HTMAXBUTTON - 最大化按钮',
    'HTLEFT - 左边界',
    'HTRIG - 右边界',
    'HTTOP - 上边界',
    'HTTOPLEFT - 左上角',
    'HTTOPRIG - 右上角',
    'HTBOTTOM - 下边界',
    'HTBOTTOMLEFT - 左下角',
    'HTBOTTOMRIG - 右下角',
    'HTBORDER',
    'HTOBJECT',
    'HTCLOSE - 关闭按钮',
    'HTHELP
    */
    CRect wrect;
    //r = HTHELP;//HTCLOSE;//HTMAXBUTTON;//test

    GetWindowRect(wrect);

    //ClientToScreen(rect);//不行,含有标题栏,所以这样换算是不准确的
    //ScreenToClient(&point);
    point.x -= wrect.left;
    point.y -= wrect.top;

    if (PtInRect(captionButtonRect_close, point))
    {
        r = HTCLOSE;
    }
    if (PtInRect(captionButtonRect_max, point))
    {
        if (IsZoomed())
        {
            r = HTZOOM;//其实和 HTMAXBUTTON 是一样的
        }
        else
        {
            r = HTMAXBUTTON;
        }
    }
    if (PtInRect(captionButtonRect_min, point))
    {
        r = HTMINBUTTON;
    }

//    printf(" point.x %d, point.y %d, rect.top %d, rect.left %d, rect.bottom %d, rect.right %d\r\n", point.x, point.y, rect.top, rect.left, rect.bottom, rect.right);

    //DrawCaption();//放到前面去,因为这个可以计算出按钮位置,然后告诉系统 ta 当前点击中的是哪个按钮
    return r;

    //--------------------------------------------------
    //return CDialog::OnNcHitTest(point);
}

clq
2010-7-20 11:15:02 发表 编辑

ZT

用WM_NCHITTEST消息欺骗Windows(一)
2007-02-02 14:26

通常,我们拖动对话框窗口的标题 栏来移动窗口,但有时候,我们想通过鼠标在客户区上拖动来移动窗口。

一个容易想到的方案是,处理鼠标 消息WM_LBUTTONDOWNWM_LBUTTONUP。 在OnLButtonUp函数中计算鼠标位置的变化,调用MoveWindow实 现窗口的移动。

注意,拖动标题栏移动窗口的时 候,会出现一个矩形框,它提示了窗口移动的当前位置。当鼠标左键放开的时候,窗口就移动到矩形框所在位置。而我们的实现方案中没有这个功能。

要实现此功能,我们必须自己来画 这些矩形。

事实上,我们没有必要自己来做这 件事情,因为Windows已经给我们做好了。

试想,如果我能够欺骗Windows, 告诉它现在鼠标正在拖动的是标题栏而不是客户区,那么窗口移动操作就由Windows来代劳了。

要欺骗Windows并 不像想像中的困难,甚至非常简单。

我们利用一个消息:WM_NCHITTEST

MSDN对 它的解释是:

The WM_NCHITTEST message is sent to a window when the cursor moves, or when a mouse button is pressed or released. If the mouse is not captured, the message is sent to the window beneath the cursor. Otherwise, the message is sent to the window that has captured the mouse.

这个消息是当鼠标移动或者有鼠标 键按下时候发出的。

Windows用 这个消息来做什么? “HITTEST”就是命 中测试的意思,WM_NCHITTEST消 息用来获取鼠标当前命中的位置。

WM_NCHITTEST的 消息响应函数会根据鼠标当前的坐标来判断鼠标命中了窗口的哪个部位,消息响应函数的返回值指出了部位,例如它可能会返回HTCAPTION, 或者HTCLIENT等。(其返回值有很多,请查阅MSDN)。

为了便于理解,我先描述一下Windows对 鼠标键按下的响应流程:

1.  确定鼠标键 点击的是哪个窗口。Windows会用表记录当前屏幕上各个窗口的区域坐标,当鼠标驱动程序通知Windows鼠 标键按下了,Windows根据鼠标的坐标确定它点击的是哪个窗口。

2.  确定鼠标键 点击的是窗口的哪个部位。Windows会向鼠标键点击的窗口发送WM_NCHITTEST消 息,来询问鼠标键点击的是窗口的哪个部位。(WM_NCHITTEST的消息响应函数的返回值会通 知Windows)。通常来说,WM_NCHITTEST消 息是系统来处理的,用户一般不会主动去处理它(也就是说,WM_NCHITTEST的消息响应函数 通常采用的是Windows默认的处理函数)。

3.  根据鼠标键 点击的部位给窗口发送相应的消息。例如:如果WM_NCHITTEST的消息响应函数的返回值是HTCLIENT, 表示鼠标点击的是客户区,则Windows会向窗口发送WM_LBUTTONDOWN消 息;如果WM_NCHITTEST的消息响应函数的返回值不是HTCLIENT(可 能是HTCAPTIONHTCLOSEHTMAXBUTTON等), 即鼠标点击的是非客户区,Windows就会向窗口发送WM_NCLBUTTONDOWN消 息。

我们有必要详细讨论一下:如果WM_NCHITTEST的 消息响应函数的返回值是HTCAPTION,即指示了鼠标点击了标题栏,接下去Windows的 处理是怎样的?

上面已经提到,接下来,Windows会 向窗口发送WM_NCLBUTTONDOWN消息。

MSDNWM_NCLBUTTONDOWN消 息描述如下:

WM_NCLBUTTONDOWN 

nHittest = (INT) wParam;    // hit-test value 

pts = MAKEPOINTS(lParam);   // position of cursor

WM_NCLBUTTONDOWNwParam指 示了鼠标点击的窗口部位,lParam指示了当前鼠标的坐标。

如果应用程序没有对该消息响应, 则由系统默认处理。

系统默认处理又是怎样的呢?系统 发现wParam指示了鼠标点击的是标题栏,就会标识当前窗口处于拖 拽状态Windows内部记录了每个窗 口的状态信息)。由于标识了拖拽状态, 则从此刻起到鼠标键放开之前,你的鼠标移动状况完全由Windows跟踪。它根据鼠标的移动,使得 窗口作“同步”移动。

注意,这个过程中,窗口不会收到WM_NCMOUSEMOVE消 息,因为窗口和鼠标是“同步”移动的,你的鼠标相对于窗口是静止的。(这些细节你可以自己写个示例来测试并分析得出,事实上我也是这么做的。如果我的观点 有错误,欢迎指正)。



clq
2010-7-20 11:17:23 发表 编辑

而且消息的返回值不一定要用默认的哪几个,可以自定义。
http://topic.csdn.net/u/20090522/10/2d25d1ba-97a7-40ce-ae65-1ef5f4eccc73.html
--------------------------------------------------
代码来了。

.h文件中:
C/C++ code
class TForm1 : public TForm
{
__published:
// IDE-managed Components
void __fastcall FormResize(TObject *Sender);

private: // User declarations
TRect m_rctCaptionBtn;
bool m_bCaptionMouseDown;
void __fastcall CrnDrawCaptionButton(bool bDown = false);
void __fastcall WMNCPaint(TWMNCPaint &Msg);
void __fastcall WMNCActivate(TWMNCActivate &Msg);
void __fastcall WMSetText(TWMSetText &Msg);
void __fastcall WMNCHitTest(TWMNCHitTest &Msg);
void __fastcall WMNCLButtonDown(TWMNCLButtonDown &Msg);
void __fastcall WMNCLButtonUp(TWMNCLButtonUp &Msg);
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_NCPAINT, TWMNCPaint, WMNCPaint)
VCL_MESSAGE_HANDLER(WM_NCACTIVATE, TWMNCActivate, WMNCActivate)
VCL_MESSAGE_HANDLER(WM_SETTEXT, TWMSetText, WMSetText)
VCL_MESSAGE_HANDLER(WM_NCHITTEST, TWMNCHitTest, WMNCHitTest)
VCL_MESSAGE_HANDLER(WM_NCLBUTTONDOWN, TWMNCLButtonDown, WMNCLButtonDown)
VCL_MESSAGE_HANDLER(WM_NCLBUTTONUP, TWMNCLButtonUp, WMNCLButtonUp)
END_MESSAGE_MAP(TForm)

public: // User declarations
__fastcall TForm1(TComponent* Owner);
};



.cpp文件中:
C/C++ code
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// 当改变窗体大小时,重新绘制按钮,否则有错位
OnResize = FormResize;

m_bCaptionMouseDown
= false;
}

#include
<Buttons.hpp>

const htCaptionBtn = HTSIZELAST + 1;

//---------------------------------------------------------------------------
void __fastcall TForm1::CrnDrawCaptionButton(bool bDown)
{
int xFrame, yFrame, xSize, ySize;
TRect r;

// Dimensions of Sizeable Frame
xFrame = ::GetSystemMetrics(SM_CXFRAME);
yFrame
= ::GetSystemMetrics(SM_CYFRAME);

// Dimensions of Caption Buttons
xSize = ::GetSystemMetrics(SM_CXSIZE);
ySize
= ::GetSystemMetrics(SM_CYSIZE);

// Define the placement of the new caption button
m_rctCaptionBtn = Bounds(Width - xFrame - 4 * xSize + 2,
yFrame
+ 2, xSize - 2, ySize - 4);

// Get the handle to canvas using Form's device context
Canvas->Handle = ::GetWindowDC(Handle);
Canvas
->Font->Name = "Marlett";
Canvas
->Font->Color = clBlue;
Canvas
->Font->Style = TFontStyles() << fsBold;
try
{
// 绘制按钮,注意第6个参数指明了是否为按下状态
DrawButtonFace(Canvas, m_rctCaptionBtn, 1, bsAutoDetect, False, bDown, False);

// 定义绘制按钮上文字的区域
r = Bounds(Width - xFrame - 4 * xSize + 3,
yFrame
+ 3, xSize - 5, ySize - 7);

// 用Marlett的字体画一个小圆点,对应的字符是i,可根据需要更改
::DrawText(Canvas->Handle, "i", 1, &r, DT_CENTER | DT_VCENTER);
}
__finally
{
::ReleaseDC(Handle, Canvas
->Handle);
Canvas
->Handle = 0;
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WMNCPaint(TWMNCPaint &Msg)
{
TForm::Dispatch(
&Msg);

CrnDrawCaptionButton();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WMNCActivate(TWMNCActivate &Msg)
{
TForm::Dispatch(
&Msg);

CrnDrawCaptionButton();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WMSetText(TWMSetText &Msg)
{
TForm::Dispatch(
&Msg);

CrnDrawCaptionButton();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WMNCHitTest(TWMNCHitTest &Msg)
{
TForm::Dispatch(
&Msg);

if (PtInRect(m_rctCaptionBtn, TPoint(Msg.XPos - Left, Msg.YPos - Top)))
Msg.Result
= htCaptionBtn;
else if (m_bCaptionMouseDown)
{
m_bCaptionMouseDown
= false;
CrnDrawCaptionButton();
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WMNCLButtonDown(TWMNCLButtonDown &Msg)
{
TForm::Dispatch(
&Msg);

// 63 63 72 75 6E 2E 63 6F 6D
if (Msg.HitTest == htCaptionBtn)
{
m_bCaptionMouseDown
= true;
CrnDrawCaptionButton(
true);
}
}
//---------------------------------------------------------------------------
// 在标题栏按钮的鼠标弹出事件中加入相应的处理代码
void __fastcall TForm1::WMNCLButtonUp(TWMNCLButtonUp &Msg)
{
TForm::Dispatch(
&Msg);
if (Msg.HitTest == htCaptionBtn)
{
// 先恢复按钮的弹出状态
CrnDrawCaptionButton();

// 加入自己的处理代码
ShowMessage("哈哈,我就是标题栏新添加的按钮");
}
}
//---------------------------------------------------------------------------
//
void __fastcall TForm1::FormResize(TObject *Sender)
{
Perform(WM_NCACTIVATE, Active,
0);
}



以上代码在Vista SP1 + C++Builder6 编译完成,测试通过。注意要在经典主题模式下才会在标题栏添加按钮。


总数:3 页次:1/1 首页 尾页  
总数:3 页次:1/1 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1