VC拖动条分割窗口模拟
1、介绍
一些常用的商用程序如迅雷、金山词霸2005,暴风影音等均有漂亮、清爽的界面,尤其是有一个漂亮的分隔条将整个窗口分割成若干区域,如下图,金山词霸2005,竖的蓝色的分割表,暴风简单的分割条将播放区和列表区分开。
照片描述:迅雷截图(水平分割窗口)
照片描述:暴风截图(垂直分割窗口)
窗口的分割有很多实现的方法,如可以用MFC的CSplitterWnd类实现窗口的分割,该方法对于SDI程序支持的非常好,对于对话框程序支持不太完善,用起来比较麻烦。本文采用鼠标消息:
鼠标移动WM_MOUSEMOVE,
鼠标左键按下WM_LBUTTONDWON,
鼠标左键松开WM_LBUTTONUP
并结合GDI绘图实现了窗口程序的分割,该方法适合任意的窗口,且分割条可以呈现任意图形(需结合GDI)。
2、原理
首先观察一下怎么使用分隔条(词霸为例):
(1)鼠标移动进入分隔条区域:此时鼠标光标切换,如果不离开该区域,鼠标光标不变,离开则复原光标。
(2)鼠标在分隔条区域中按下
此时若左键按下:(鼠标光标可以改变),则开始绘制拖动的虚线框,若不松开,则移动鼠标,虚线框跟着移动,若松开,则分割位置重新定位
(3)鼠标松开
若此时鼠标按住flag为真,则表明是在分隔条区域中按下后松开的(可能移动过),此时重新定位分隔条位置,并重绘分隔条以及相应改变的窗口。
为了实现该功能,需要控制变量和拖动区域相关变量:
l 是否在区域,是否鼠标左键按住
l 拖动区域:位置、宽度,虚线框位置及宽度(可以和拖动区域宽相等)
可以用两个BOOL变量m_bIsInBarRgn, m_bIsMouseDowning分别表示是否在区域中和是否左键按住。
(a) BOOL m_bIsInBarRgn;
默认为FALSE
若第一次进入该区域[1][1],就设置为TRUE,离开该区域则为FALSE
鼠标在移动的过程中,反复检查鼠标点是否在区域中,
若在区域中,如果该值为TRUE,表示鼠标移动时,没有离开该区域,不用改变鼠标光标(已经换过了)
如果该值为FALSE,表示鼠标移动时,进入该区域,要改变鼠标图标
(b) BOOL m_bIsMouseDowning;
默认值FALSE,表示鼠标松开,若TRUE:鼠标按住
如果鼠标按下,且鼠标点在拖动条区域中(此时m_bIsInBarRgn==TRUE),如果该值为FALSE,则变为TRUE;
鼠标松开时(up),若该值为TRUE,则变为FALSE。
(c)
int m_nPos;
拖动条在客户区的位置
初始化给定一个值
鼠标按住区域拖动时,若松开,则改变该值(可以控制范围)
int m_nXPos;
拖动虚线框所在位置
初始化值和m_nPos值相同。
拖动时,抹去上次画的虚线框区域,画当前虚线框区域,改变该值为新的移动位置。
int m_nWidthDragBar;
拖动条窗口的宽,也是拖动虚线框的宽,如果在拖动条窗口画图像,则和图像的宽一样。
为了实现分隔条的绘制以及虚线框的绘制,需要提供对应的两个函数:
分隔条的绘制
void DrawDragBar(CDC* pDC);
void CDragBarDemoDlg::DrawDragBar(CDC* pDC)
{
CRect rc;
GetClientRect(rc);
// CRect rcBar(m_nPos-1,0,m_nPos+m_nWidthDragBar,rc.Height());
// CBrush brBar(RGB(100,100,255));
// pDC->FrameRect(&rcBar,&brBar);
CBitmap bmp;
bmp.LoadBitmap(IDB_BAR);
BITMAP sBmp;
bmp.GetBitmap(&sBmp);
CDC mdc;
mdc.CreateCompatibleDC(pDC);
CBitmap* oldBmp = mdc.SelectObject(&bmp);
pDC->StretchBlt(m_nPos,0,sBmp.bmWidth,rc.Height(),&mdc,0,0,sBmp.bmWidth,sBmp.bmHeight,SRCCOPY);
mdc.SelectObject(oldBmp);
mdc.DeleteDC();
bmp.DeleteObject();
}
虚线框的绘制
void DrawXBar(CDC* pDC,int nXNew);
void CDragBarDemoDlg::DrawXBar(CDC* pDC,int nXNew)
{
int nXOld = m_nXPos;
CRect rc;
GetClientRect(rc);
int c = 100;
CPen pen(PS_SOLID,1,RGB(c,c,c));
int nW = 3;
CRect rcOld(nXOld,0,nXOld+nW,rc.Height());
InvalidateRect(rcOld,FALSE);
UpdateWindow();
CRect rcNew(nXNew,0,nXNew+nW,rc.Height());
CBrush* oldBr = (CBrush*)pDC->SelectStockObject(NULL_BRUSH);
CPen* oldPen = pDC->SelectObject(&pen);
pDC->Rectangle(rcNew);
pDC->SelectObject(oldPen);
pDC->SelectObject(oldBr);
//save now xbar pos
m_nXPos = nXNew;
}
3、实现(以对话框为例)
(1)在对话框类中添加对应变量和函数声明
HCURSOR m_hCursorMouseOver; //鼠标光标
HCURSOR m_hCursorMouseDowning;
int m_nPos; //拖动条在客户区的位置
int m_nXPos; //拖动框所在位置
int m_nWidthDragBar;//拖动条窗口的宽
void DrawDragBar(CDC* pDC);
void DrawXBar(CDC* pDC,int nXNew);
BOOL m_bIsInBarRgn;
BOOL m_bIsMouseDowning; //==FALSE,default,鼠标松开,==TRUE:鼠标按住
(2)初始化
// TODO: Add extra initialization here
CRect rc;
GetClientRect(rc);
m_nPos = 100; //rc.Width()/3;
m_nXPos = m_nPos;
m_nWidthDragBar = 5; //和拖动条图像的宽度一样
m_bIsInBarRgn = FALSE;
m_bIsMouseDowning = FALSE;
m_hCursorMouseOver = theApp.LoadCursor(IDC_MOUSE_OVER);
m_hCursorMouseDowning = theApp.LoadCursor(IDC_MOUSE_DOWN);
(3)绘制OnPaint函数
void CDragBarDemoDlg::OnPaint()
{
if (IsIconic())
{
。。。。。。。。默认代码
}
else
{//新添加代码
CPaintDC dc(this);
//以拖动条为边界,一分为二,左边区域填充为红色,右边填充为绿色
CRect rc;
GetClientRect(rc);
CRect rcLeft(0,0,m_nPos,rc.Height());
CRect rcRight(m_nPos+m_nWidthDragBar,0,rc.Width(),rc.Height());
CBrush brLeft(RGB(255,123,123));
CBrush brRight(RGB(123,255,123));
dc.FillRect(&rcLeft,&brLeft);
dc.FillRect(&rcRight,&brRight);
// int nMode = dc.SetBkMode(TRANSPARENT);
// dc.DrawText("左边区域",&rcLeft,DT_SINGLELINE|DT_VCENTER|DT_CENTER);
// dc.DrawText("右边区域",&rcRight,DT_SINGLELINE|DT_VCENTER|DT_CENTER);
// dc.SetBkMode(nMode);
DrawDragBar(&dc);
CDialog::OnPaint();
}
}
(4)鼠标移动:添加WM_MOUSEMOVE消息处理函数
extern CDragBarDemoApp theApp;
void CDragBarDemoDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CRect rc;
GetClientRect(rc);
CRect rcBar(m_nPos,0,m_nPos+m_nWidthDragBar,rc.Height());
CClientDC dc(this);
if(rcBar.PtInRect(point)) //点在拖动条区域内
{
if(m_bIsInBarRgn == FALSE) //第一次进入该区域
{
SetCursor(m_hCursorMouseOver);
SetCapture();
m_bIsInBarRgn = TRUE;
}
else
{
if(m_bIsMouseDowning) //如果在该区域内移动,且按住了鼠标左键
{
//不用重画
}
}
}
else//点不在该区域,检查是不是第一次离开该区域
{
if(m_bIsInBarRgn == TRUE) //第一次离开
{
if (m_bIsMouseDowning) //按住鼠标左键离开
{
//限定移动的范围
if(point.x<50)
{
point.x = 50;
CPoint pt(50,point.y);
ClientToScreen(&pt);
SetCursorPos(pt.x,pt.y); //移动光标位置
}
if(point.x>180)
{
point.x = 180;
CPoint pt(180,point.y);
ClientToScreen(&pt);
SetCursorPos(pt.x,pt.y);
}
//----------------------------
DrawXBar(&dc,point.x);
}
else//真正的离开
{
ReleaseCapture();
SetCursor(theApp.LoadStandardCursor(IDC_ARROW));
m_bIsInBarRgn = FALSE;
}
}
}
CDialog::OnMouseMove(nFlags, point);
}
(5)鼠标左键按下 WM_LBUTTONDOWN
void CDragBarDemoDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
if(m_bIsInBarRgn)
{
CClientDC dc(this);
//SetCursor(//m_hCursorMouseDowning);//设置鼠标按住时的光标
//SetCapture();
DrawXBar(&dc,point.x);
m_bIsMouseDowning = TRUE;
}
CDialog::OnLButtonDown(nFlags, point);
}
(6)鼠标左键松开 WM_LBUTTONUP
void CDragBarDemoDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
if(m_bIsMouseDowning)
{
m_bIsMouseDowning = FALSE;
m_nPos = point.x;
Invalidate();
}
CDialog::OnLButtonUp(nFlags, point);
}