普通的Windows应用程序窗口都具有统一的窗口风格,它们一般都包括:用户区和非用户区两部分。非用户区包括标题条、窗口边框、最大化按钮、最小化按钮、关闭按钮和系统默认的快捷键及鼠标支持等。利用鼠标的左键通过拖动窗口的标题条可以在屏幕上任意移动整个窗口,当光标停止在窗口边框上时可以改变窗口的大小。在一些比较经典的软件中,有一类特殊的无标题条的窗口。这种窗口不需要改变窗口的大小,但必须具有窗口客户区域的拖动功能,典型的例子有 Windows中的IME输入法应用程序、中文之星以及一些第三方开发的输入法平台等。由于普通窗口的拖动功能是由系统来完成的,编制一般的应用程序时根本无须考虑客户区域拖动问题,但对于无标题条的Windows应用程序,拖动功能就要靠开发者自己来实现了。
实现 思路
既然Windows已经实现了不错的窗口拖动功能,我们可以通过利用系统这种默认的实现来完成无标题条的窗口的拖动。仔细分析一下,不难发现,无标题条窗口的客户区大都只是作为控件的载体(最常见到的是按钮控件),当鼠标位于其窗口中时(鼠标变为十字箭头)可以拖动整个窗口,而且窗口的大小不可改变,这很像一般窗口标题条的功能,因此只要将无标题条窗口模拟成标题条,使系统将其“误认为”是窗口的标题条,就可以利用系统的默认实现来完成无标题条窗口的拖动了。
那么如何使系统将无标题条窗口的客户区“误认为”是窗口的标题条呢?重载函数:
UINT CWnd::OnNcHitTest(CPoint point)
每当鼠标在窗口中移动,那么MFC的应用程序框架会发送消息WM_NCHITTEST,由该函数来完成对消息处理。其中参数point鼠标当前所在位置坐标,函数的返回值为:
只要重载无标题窗口的OnNcHitTest函数,使其返回值总为HTCAPTION,就使系统将无标题条窗口的客户区“误认为”是窗口的标题条。
程序 实现
用VC++5.0的MFC AppWizad(exe) 生成一个对话框应用框架,然后完成下面的修改:
1、在对话框资源的属性中,不选Title Bar。
2、在DragDlg.h 文件中添加:
// Generated message map functions
//{{AFX_MSG(CDragDlg)
...
afx_msg UINT OnNcHitTest(CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
3、在DragDlg.cpp文件中添加:
BEGIN_MESSAGE_MAP(CDragDlg, CDialog)
//{{AFX_MSG_MAP(CDragDlg)
// No message handlers
...
ON_WM_NCHITTEST()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
UINT CDragDlg::OnNcHitTest(CPoint point)
{
CWnd::OnNcHitTest(point);
return HTCAPTION;
}
进一步完善这个程序,可以加入对鼠标的处理,当鼠标位于窗口中时,自动变为十字箭头,提示用户现在可以拖动窗口。注意,由于此时窗口被系统“误认为”是标题条(非用户区), 相应的鼠标移动和鼠标左键按下消息应为 WM_NCMOUSEMOVE和WM_NCLBUTTONDOW(通过VC++的工具spy++可以观察到),具体的实现这里就不详述。
附:(我在C++ Builder下实现的代码)
.H文件:
//---------------------------------------------------------------------------
#ifndef MonitorWindowH
#define MonitorWindowH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
#include <Graphics.hpp>
#include <winuser.h>
#include <Buttons.hpp>
#define WS_EX_LAYERED 0x00080000
#define LWA_ALPHA 0x00000002
//---------------------------------------------------------------------------
class TMonitorForm : public TForm
{
__published: // IDE-managed Components
TPanel *Panel1;
TStatusBar *StatusBar1;
TListView *ListView;
TImage *Image1;
TBitBtn *BitBtnCancel;
TBitBtn *BitBtnCance;
void __fastcall Image1MouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y);
void __fastcall BitBtnCanceClick(TObject *Sender);
void __fastcall BitBtnCancelClick(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TMonitorForm(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TMonitorForm *MonitorForm;
//---------------------------------------------------------------------------
#endif
.CPP文件:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MonitorWindow.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMonitorForm *MonitorForm;
//---------------------------------------------------------------------------
__fastcall TMonitorForm::TMonitorForm(TComponent* Owner)
: TForm(Owner)
{
SetWindowLong(Handle,GWL_EXSTYLE,GetWindowLong(Handle,GWL_EXSTYLE)^WS_EX_LAYERED);
SetLayeredWindowAttributes(Handle,RGB(0,0,0),180,LWA_ALPHA);
}
//---------------------------------------------------------------------------
void __fastcall TMonitorForm::Image1MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
ReleaseCapture();
//SendMessage(this->Handle,WM_SYSCOMMAND,SC_MOVE+HTCAPTION,0);
SendMessage(Handle,WM_NCLBUTTONDOWN,HTCAPTION,0);
}
//---------------------------------------------------------------------------
void __fastcall TMonitorForm::BitBtnCanceClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
void __fastcall TMonitorForm::BitBtnCancelClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------