原作者:http://blog.163.com/toplcx@yeah/blog/static/92667383201021932360/
原理: 创建一个单独的非模态对话框,并将其置顶
1、 在解决方案中添加对话框资源 ID:IDD_FLOAT 属性 Border设为none,Title Bar设为false。为对话框模板添加基于CDialog的类CFloatDlg,为CMainFrame添加指针变量m_pFdlg
2、 在CMainFrame::On
m_pFdlg = new CFloatDlg();
m_pFdlg->Create( IDD_FLOAT, NULL);//
CRect rect;
m_pFdlg->GetWindowRect( &rect );
m_pFdlg->SetWindowPos( &wndTopMost, rect.left, rect.top, rect.Width(), rect.Height(), SWP_SHOWWINDOW );//置顶显示
// GetSystemMetrics(SM_CXSCREEN)获得屏幕的宽度,此句设置悬浮窗初始屏幕位置
m_pFdlg->MoveWindow( GetSystemMetrics(SM_CXSCREEN) - 70, 70, rect.Width(), rect.Height(), TRUE )
m_pFdlg->ShowWindow(SW_SHOW); //显示对话框
m_pFdlg->UpdateWindow();
3、最后不要忘了释放释放资源
void CMainFrame::On
{ //销毁对话框
delete dlg;
CFrameWnd::On
}
至此已经出现了一个非模态对话框 接下来对其进行一下初始化使其更像悬浮框
1、 隐藏悬浮窗在任务栏上的显示
在CFloatDlg::On
::SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, WS_EX_TOOLWINDOW); //隐藏对话框在任务栏上的显示
2、 半透明化
同上在初始化中添加
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(),124,128,2); //2代表透明化的程度5为完全透明
FreeLibrary(hInst);
}
3、 添加图片,添加一个位图资源 直接在对话框中放置一个Picture ControL显示图片即可
4、 设置悬浮框的大小,在对话框类中响应WM_SIZE消息,SetWindowPos(NULL,0,0,40,40,SWP_NOREPOSITION);
5、 实现左击拖动悬浮窗分别响应左键按下抬起 以及鼠标移动
类添加BOOL变量 m_bLBDown,指示左键有没有按下,CPoint变量m_ptLBBegin,记录左键按下时的窗口坐标
void CFloatDlg::On
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_bLBDown=TRUE;
m_ptLBBegin=point;
CDialog::On
}
void CFloatDlg::On
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_bLBDown)
{
CPoint pt;
::GetCursorPos(&pt);
MoveWindow(pt.x-m_ptLBBegin.x,pt.y-m_ptLBBegin.y,40,40);
CDialog::On
CRect rect;
GetWindowRect(&rect);
ClipCursor(&rect);
}
CDialog::On
}
void CFloatDlg::On
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
ClipCursor(NULL);
m_bLBDown=FALSE;
}
6、 至于其他的消息响应 比如右击弹出菜单等,就像一般普通的对话框一样重载响应的消息即可。
仿制 360 桌面悬浮框
1、目标
实现类似360悬浮窗口这样的效果,当窗口在屏幕边缘时,鼠标移开,就自动向边缘隐藏,鼠标放上去,就又平滑显示出来。
正常状态:
边缘自动隐藏:
2,原理
首先是实现圆角或椭圆这种不规则形状的窗口,可以参考另一篇文章:
然后需要给没有标题栏的窗口增加拖拽移动的功能,这个就是自己手动发送一个消息,使windows认为鼠标在标题条上
对于窗口的移动显示隐藏,使用了定时器。
其中有一些做判断的函数,如判断在窗口在屏幕某个边缘,判断鼠标是否在窗口内部等。
3,实现
①新建MFC对话框程序Test360.去掉默认控件和属性中的边框。参考上面所说的文章实现一个带圆角及背景图片的窗口。
由于这里还是截图然后用PS简单选择了个范围,所以还有毛边,若是有美工原图或PS仔细些,是没问题的。
②给Dlg类CTest360Dlg添加一条消息响应OnLButtonDown,在其中传送WM_NCLBUTTONDOWN消息,达到拖动效果。
- void CTest360Dlg::OnLButtonDown(UINT nFlags, CPoint point)
- {
- CDialog::OnLButtonDown(nFlags, point);
- // 实现拖动窗口
- // 发送WM_NCLBUTTONDOWN消息
- // 使Windows认为鼠标在标题条上
- // 或SendMessage(WM_SYSCOMMAND,SC_MOVE | HTCAPTION,0);
- PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x, point.y));
- }
③添加几个判断窗口是否在屏幕边缘的函数:
- //是否靠近屏幕左边缘
- BOOL CTest360Dlg::NearLeftBorder()
- {
- CRect rc;
- GetWindowRect(rc);
- //窗口左边界在屏幕左边界20像素内都算“靠近”
- if (rc.left < 20)
- {
- return TRUE;
- }
- return FALSE;
- }
- //是否靠近屏幕上边缘
- BOOL CTest360Dlg::NearUpBorder()
- {
- CRect rc;
- GetWindowRect(rc);
- if(rc.top<20)
- {
- return TRUE;
- }
- return FALSE;
- }
- //是否靠近右边缘
- BOOL CTest360Dlg::NearRightBorder()
- {
- CRect rc;
- GetWindowRect(rc);
- int nWidth = GetSystemMetrics(SM_CXSCREEN);
- if (rc.left>nWidth - rc.Width())
- {
- return TRUE;
- }
- return FALSE;
- }
④判断鼠标是否在窗口内。
- BOOL CTest360Dlg::MouseInWnd()
- {
- CRect rc;
- GetWindowRect(rc);
- POINT pt;
- GetCursorPos(&pt);
- if (PtInRect(&rc,pt))
- {
- return TRUE;
- }
- return FALSE;
- }
- #define TIMER_MOVE 1
- BOOL CTest360Dlg::OnInitDialog()
- {
- CDialog::OnInitDialog();
- // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
- // 执行此操作
- SetIcon(m_hIcon, TRUE); // 设置大图标
- SetIcon(m_hIcon, FALSE); // 设置小图标
- //设置窗口形状
- SetRegion(GetDC(),IDB_BITMAP_360RGN,RGB(0,0,0));
- //初始时居中
- CenterWindow();
- //设置定时器,处理悬浮窗的显隐移动
- SetTimer(TIMER_MOVE,10,NULL);
- return TRUE;
- }
处理如下:
- void CTest360Dlg::OnTimer(UINT_PTR nIDEvent)
- {
- if (nIDEvent == TIMER_MOVE)
- {
- //鼠标按着的,就怎么都不移动
- if (GetKeyState(VK_LBUTTON)<0)
- {
- return;
- }
- //靠近屏幕上边缘
- if (NearUpBorder())
- {
- //根据鼠标动作进行窗口的移动(鼠标进入区域就向下平移显示,鼠标离开就向上平移隐藏)
- MoveUp();
- return;
- }
- //靠近屏幕左边缘
- if (NearLeftBorder())
- {
- //根据鼠标动作进行窗口的移动(鼠标进入区域就向右平移显示,鼠标离开就向左平移隐藏)
- MoveLeft();
- return;
- }
- //靠近屏幕右边缘
- if (NearRightBorder())
- {
- //根据鼠标动作进行窗口的移动(鼠标进入区域就向左平移显示,鼠标离开就向右平移隐藏)
- MoveRight();
- return;
- }
- }
- CDialog::OnTimer(nIDEvent);
- }
其中GetKeyState先强行过滤掉鼠标按下,让这种情况不移动。避免刚拖动窗口到屏幕边缘时鼠标还没松开就直接开始移动了。
3个Move函数,是真正按像素移动窗口的地方,包括来回(出屏幕和进屏幕)。原理是一样的,看明白一个就OK了。
- void CTest360Dlg::MoveUp()
- {
- CRect rc;
- GetWindowRect(rc);
- //鼠标进入则下移,显示出来
- if(MouseInWnd())
- {
- int height = rc.Height();
- if (rc.top>=0)
- {
- rc.top = 0;
- }
- else
- {
- rc.top++;
- }
- rc.bottom = rc.top + height;
- MoveWindow(rc);
- }
- //鼠标在别处,窗口就往上移出屏幕
- else
- {
- int height = rc.Height();
- //窗口向上移动一像素,如果快隐藏(露20)就不移了
- if (rc.top<= 20 - height)
- {
- rc.top = 20 - height;
- ShowWindow(SW_HIDE);
- m_upDlg->m_Test360Dlg = this;
- m_upDlg->DoModal();
- }
- else
- {
- rc.top--;
- }
- rc.bottom = rc.top + height;
- MoveWindow(rc);
- }
- }
- void CTest360Dlg::MoveLeft()
- {
- CRect rc;
- GetWindowRect(rc);
- //鼠标进入则下移,显示出来
- if(MouseInWnd())
- {
- int width = rc.Width();
- if (rc.left>=0)
- {
- rc.left = 0;
- }
- else
- {
- rc.left++;
- }
- rc.right = rc.left + width;
- MoveWindow(rc);
- }
- //鼠标在别处,窗口就往上移出屏幕
- else
- {
- int width = rc.Width();
- //窗口向左移动一像素,如果快隐藏(留20像素)就不移了
- if (rc.left<= 20 - width)
- {
- rc.left = 20 - width;
- }
- else
- {
- rc.left--;
- }
- rc.right = rc.left + width;
- MoveWindow(rc);
- }
- }
- void CTest360Dlg::MoveRight()
- {
- CRect rc;
- GetWindowRect(rc);
- int sysWidth = GetSystemMetrics(SM_CXSCREEN);
- //鼠标在窗口内则窗口左移,显示出来
- if(MouseInWnd())
- {
- int width = rc.Width();
- if (rc.left<= sysWidth - width)
- {
- rc.left = sysWidth - width;
- }
- else
- {
- rc.left--;
- }
- rc.right = rc.left + width;
- MoveWindow(rc);
- }
- //鼠标没在窗口上,窗口就往右移出屏幕
- else
- {
- int width = rc.Width();
- //窗口向右移动一像素,如果快隐藏了(还留20像素)就不移了
- if (rc.left>= sysWidth - 20)
- {
- rc.left = sysWidth - 20;
- }
- else
- {
- rc.left++;
- }
- rc.right = rc.left + width;
- MoveWindow(rc);
- }
- }
当Timer中判断到窗口在屏幕上边缘时,进入MoveUp,如果此时鼠标进入窗口内,窗口就往下方移动直到完全显示;如果鼠标离开窗口,那么窗口会立即往上隐藏,直到留下一小截。 360官方软件现在是换了个半圆形的窗口“趴”在屏幕边上。这里主要是模拟触发移动的效果。
4,效果
几张截图
左侧:
上侧:
右侧:
5,源码
MFC模拟360悬浮窗加速球Test360_VS2008工程.rar