一个更COOL的透明浮动窗口
引子:
大家看过http://www.vccode.com上那篇文章《一个很酷COOL的透明浮动窗口》(http://www.vccode.com/file_show.php?id=2330)。我进行了一些改进,解决了作者在文章最后遗留下的那个问题,并实现快捷菜单功能。以下格式仿此文章的格式,并有一部分从其中摘录(偶人比较懒,嘿嘿),在此感谢作者。
正文:
本文分析了Windows环境下使用MFC实现透明窗口,以及浮动窗口的方法
关键词: 透明窗口,浮动窗口。
WIN2000以上操作系统,VC++6.0。
如何实现透明窗口
使用MFC的AppWizard生成一个基于对话框的工程,删除Didalog上的所有控件,并设置其附加属性Extended Styles为 Tool Window。
使用SetLayeredWindowAttributes可以方便的制作透明窗体,此函数在w2k以上才支持,而且如果希望直接使用的话,可能需要下载最新的SDK。不过此函数在w2k的user32.dll里有实现,所以如果你不希望下载巨大的sdk的话,可以直接使用GetProcAddress获取该函数的指针。
SetLayeredWindowAttributes的函数原型如下:
BOOL SetLayeredWindowAttributes(
HWND hwnd, // handle to the layered window
COLORREF crKey, // specifies the color key
BYTE bAlpha, // value for the blend function
DWORD dwFlags // action
);
Windows NT/2000/XP: Included in Windows 2000 and later.
Windows 95/98/Me: Unsupported.(注意了,在win9x里没法使用的)
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.
一些常量:
WS_EX_LAYERED = 0x80000;
LWA_ALPHA = 0x2;
LWA_COLORKEY=0x1;
其中dwFlags有LWA_ALPHA和LWA_COLORKEY
LWA_ALPHA被设置的话,通过bAlpha决定透明度.
LWA_COLORKEY被设置的话,则指定被透明掉的颜色为crKey,其他颜色则正常显示.
要使使窗体拥有透明效果,首先要有WS_EX_LAYERED扩展属性(旧的sdk没有定义这个属性,所以可以直接指定为0x80000).
例子代码:
BOOL CFloatDlgDlg::OnInitDialog()
{
CDialog::OnInitDialog();
///////////////////////////控制窗体大小和位置/////////////////////////////////////////////
CRect rect;
CWnd *pWnd = this->GetDesktopWindow();
pWnd->GetWindowRect(&rect);
this->MoveWindow(rect.left + rect.Width() - 200, rect.top + 100, 42, 42);
SetWindowPos(&wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application’s main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
////////////////////////以下实现窗体的透明///////////////////////////////////
//加入WS_EX_LAYERED扩展属性
SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,
GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)^0x80000);
HINSTANCE hInst = LoadLibrary("User32.DLL");
if(hInst)
{
typedef BOOL (WINAPI *MYFUNC)(HWND,COLORREF,BYTE,DWORD);
MYFUNC fun = NULL;
//取得SetLayeredWindowAttributes函数指针
fun=(MYFUNC)GetProcAddress(hInst, "SetLayeredWindowAttributes");
if(fun)fun(this->GetSafeHwnd(),0,128,2);
FreeLibrary(hInst);
}
//////////////////////////////////////////////////////////////////////////////////
ModifyStyle( WS_CAPTION, WS_MINIMIZEBOX, SWP_DRAWFRAME );//设置图标
m_bmpBackground.LoadBitmap(IDB_FLOAT);
return TRUE; // return TRUE unless you set the focus to a control
}
如何实现浮动窗口的拖动
一般的窗体,只有鼠标位于非客户区是,才可拖动。因此,只要我们将客户区“变”为“非客户区”不就可以拖动了了吗!真是一个让人惊奇的发现!:),可是问题来了,非客户区无法加载快捷菜单,我们如何解决这一问题呢,那我们想想什么动作触发快捷菜单呢?对了,WM_RBUTTONDOWN,WM_RBUTTONUP,那拖动呢?哈哈,是WM_LBUTTONDOWN,OK,那就有方法解决了,那就是在鼠标左键按下时才将客户区“变”为“非客户区”。重载OnLButtonDown(UINT nFlags, CPoint point) ,OnLButtonUp(UINT nFlags, CPoint point),OnMouseMove(UINT nFlags, CPoint point),OnNcHitTest(CPoint point):
void CFloatDlgDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_oldpoint = point;
SetCapture();
drag = TRUE;
CDialog::OnLButtonDown(nFlags, point);
}
void CFloatDlgDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
ReleaseCapture();
m_oldpoint = CPoint(0, 0);
drag = FALSE;
CDialog::OnLButtonUp(nFlags, point);
}
void CFloatDlgDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (m_oldpoint != CPoint(0, 0))
{
CPoint pt;
GetCursorPos(&pt);
MoveWindow(pt.x - 20, pt.y - 20, 42 ,42 );
}
CDialog::OnMouseMove(nFlags, point);
}
UINT CFloatDlgDlg::OnNcHitTest(CPoint point)
{
// TODO: Add your message handler code here and/or call default
//在鼠标左键按下时才将客户区“变”为“非客户区”
if (drag)
{
UINT nHitCode = CWnd::OnNcHitTest(point);
if (nHitCode == HTCLIENT)
{
nHitCode = HTCAPTION;
}
return nHitCode;
}
else
{
return CDialog::OnNcHitTest(point);
}
return CDialog::OnNcHitTest(point);
}
右键菜单的实现
建立一个MENU资源,重载OnContextMenu(CWnd* pWnd, CPoint point):
void CFloatDlgDlg::OnContextMenu(CWnd* pWnd, CPoint point)
{
if (point.x == -1 && point.y == -1){
//keystroke invocation
CRect rect;
GetClientRect(rect);
ClientToScreen(rect);
point = rect.TopLeft();
point.Offset(5, 5);
}
CMenu menu;
VERIFY(menu.LoadMenu(IDR_FLOAT_MENU));
CMenu* pPopup = menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
CWnd* pWndPopupOwner = this;
while (pWndPopupOwner->GetStyle() & WS_CHILD)
pWndPopupOwner = pWndPopupOwner->GetParent();
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
pWndPopupOwner);
}
其他
为了美观,我将次Dialog的标题栏去掉,并且以一个图片作为它的背景,这样就实现了FlashGet的浮动窗口的效果。
背景图片:
BOOL CFloatDlgDlg::OnInitDialog()
{
CDialog::OnInitDialog();
……….//省略
//背景图片的加载
ModifyStyle( WS_CAPTION, WS_MINIMIZEBOX, SWP_DRAWFRAME );//设置图标
m_bmpBackground.LoadBitmap(IDB_FLOAT);
return TRUE; // return TRUE unless you set the focus to a control
}
重载OnPaint()
void CFloatDlgDlg::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect(&rect);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
BITMAP bitMap;
m_bmpBackground.GetBitmap(&bitMap);
CBitmap *pbmpOld=dcMem.SelectObject(&m_bmpBackground);
dc.StretchBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,bitMap.bmWidth,bitMap.bmHeight,SRCCOPY);
}
以上是偶的一些大致介绍,具体请看源码,偶觉得还可以,各位大虾有什么意见提出来,大家共同探讨!