超酷透明窗口

一个更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。不过此函数在w2kuser32.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;
  

其中dwFlagsLWA_ALPHALWA_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_RBUTTONDOWNWM_RBUTTONUP,那拖动呢?哈哈,是WM_LBUTTONDOWNOK,那就有方法解决了,那就是在鼠标左键按下时才将客户区“变”为“非客户区”。重载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);

}

以上是偶的一些大致介绍,具体请看源码,偶觉得还可以,各位大虾有什么意见提出来,大家共同探讨!

你可能感兴趣的:(透明)