引子:
大家看过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);
-
-
- 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);
- }
- }
-
-
- SetIcon(m_hIcon, TRUE);
- SetIcon(m_hIcon, FALSE);
-
-
-
- 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;
-
- 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;
- }
如何实现浮动窗口的拖动
一般的窗体,只有鼠标位于非客户区是,才可拖动。因此,只要我们将客户区“变”为“非客户区”不就可以拖动了了吗!真是一个让人惊奇的发现!:),可是问题来了,非客户区无法加载快捷菜单,我们如何解决这一问题呢,那我们想想什么动作触发快捷菜单呢?对了,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)
- {
-
- m_oldpoint = point;
- SetCapture();
- drag = TRUE;
- CDialog::OnLButtonDown(nFlags, point);
- }
- void CFloatDlgDlg::OnLButtonUp(UINT nFlags, CPoint point)
- {
-
- ReleaseCapture();
- m_oldpoint = CPoint(0, 0);
- drag = FALSE;
- CDialog::OnLButtonUp(nFlags, point);
- }
-
- void CFloatDlgDlg::OnMouseMove(UINT nFlags, CPoint point)
- {
-
- 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)
- {
-
-
- 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){
-
- 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;
- }
-
-
- 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);
- }
以上是偶的一些大致介绍,具体请看源码,偶觉得还可以,各位大虾有什么意见提出来,大家共同探讨!
(秩名) |