效果图:
整体思路:
以CSplitterWnd为基类继承自己的窗口分割类CMySplitter
重写其中的方法(视图分割与还原功能实现):
afx_msg voidOnLButtonDown(UINT nFlags, CPoint point);
afx_msg voidOnMouseMove(UINT nFlags, CPoint point);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINTnHitTest, UINT message);
以上这三个函数的重载实际上主要是为了屏蔽原CSplitterWnd类对这三种消息的响应
其达到的效果是:用户对分割的视图做任何的改动。
afx_msg voidOnLButtonUp(UINT nFlags, CPoint point);
重写这个函数是实现功能所在:
因为在以静态切分窗口的方法中,原CSplitterWnd中的以上四个消息响应函数的组合就能够实现切分条的左右移动(以只有列切分条为例说明),所以在重写的OnLButtonUp函数中完成这一功能,只是将切分条的位置固定在整个视图的一半处,实现整个视图的切分。当要还原视图时,再将窗口还原为原来整个视图的大小。
具体做法:
1)在原工程中添加类CMySplitter,基类为:CSplitterWnd(在MFC中无此基类,需手工写入)
重写其中的方法(视图分割与还原功能实现):
afx_msg voidOnLButtonDown(UINT nFlags, CPoint point);
afx_msg voidOnMouseMove(UINT nFlags, CPoint point);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINTnHitTest, UINT message);
将以上这三个函数的消息响应直接交由CWnd类来处理:
即将原来的CSplitterWnd改为CWnd,如下面的函数:
voidCMySplitter::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnLButtonDown(nFlags,point);
}
在OnLButtonUp函数中完成窗口切分的功能:
void CMySplitter::OnLButtonUp(UINT nFlags, CPoint point)
{
if(!(point.x==0 && point.y==0))//用于判断是否为按钮按键消息,是则处理
return;
//当前窗口客户区
CRectrectClient;
GetClientRect(&rectClient);
int width = rectClient.Width();
int height = rectClient.Height();
//释放鼠标
ReleaseCapture();
//保存原视图
CWnd*pOldActiveView = GetActivePane();
// 切分条所在的区域
GetHitRect(201,m_rectTracker);
//移动切分条至客户区中间处
m_rectTracker.OffsetRect((-width/2)*m_split , 0);
// 视图设置
TrackColumnSize(m_rectTracker.left,0);//第0列(左边)设置为m_rectTracker.left宽度
RecalcLayout();
if (pOldActiveView == GetActivePane())
{
if (pOldActiveView != NULL)
{
SetActivePane(-1,-1, pOldActiveView); // re-activate
pOldActiveView->SetFocus();// make sure focus is restored
}
}
m_split = -m_split;
return;
}
其中,成员变量m_split用于表示窗口当前状态:1表示未分割,-1表示已分割
2)在框架类CMainFrame中添加函数:
OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext*pContext)//用于完成视图的初次切分
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCTlpcs, CCreateContext* pContext)
{
if(!m_wndSplitter.CreateStatic(this, 1, 2)) //创建1行2列的视图切割²
{
TRACE0("Failed to CreateStaticSplitter\n");
return FALSE;
}
CRectrc; //获得客户区大小
GetClientRect(rc);
int x=rc.Width();
int y=rc.Height();
if (!m_wndSplitter.CreateView(0, 1,RUNTIME_CLASS(CWndDraw), CSize(0,0), pContext)) {
TRACE0("Failed to create right pane\n");
return FALSE;
} /创建右侧视图,其视图类为CWndDraw
if (!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CWndDraw1), CSize(x,y), pContext))/ {
TRACE0("Failed to create left pane\n");
return FALSE;
} //创建左侧视图,其视图类为CWndDraw1
return true;//这里只能用return true 不能用原来的
}
OnSplitWnd()//按键响应函数,可以根据需要改成其他事件响应函数,在其中发出CMySplitter类的消息WM_LBUTTONUP用于调用在CMySplitter类中已经重写过的OnLButtonUp函数。
void CMainFrame::OnSplitWnd()
{
this->m_wndSplitter.SendMessage(WM_LBUTTONUP);
}
3) 为工程添加两个视图分别关联的视图类(基类为CView) CWndDraw和CWndDraw1
在CMainFrame中包含其头文件。
4) 解决窗口大小改变时(最大化、最小化用户鼠标拖动拉伸等)窗口视图的分割大小不能根据当前情况进行调整的问题:
思路:
在窗口改变时,重写CMainFrame类中的重画函数onSize()进行改写,调用CMySplitter类中的响应函数进行视图的重新绘制.
具体方法:
当窗口进行重绘时,会发送消息WM_SIZE,只要在CMainFrame类中的onSize()函数中给CMySplitter类发送WM_SIZ消息,再重新改写CMySplitter类中的onSize()函数就可以了,要注意在窗口未建立之前不能够调用关于切分条移动和切分窗口有关的函数,因为此时客户区为(0,0,0,0)如果进行绘制会发生错误。
而onSize()函数会在窗口建立之前调用一次,所以在CMainFrame和CMySplitter中分别添加标志m_WndIsVlid和m_ViewIsValid来进行相应的判断。
代码如下:
CMainFrame:
void CMainFrame::OnSize(UINT nType,intcx, int cy)
{
CFrameWndEx::OnSize(nType,cx, cy);
if(m_WndIsValid)//只在窗口框架已经存在的情况下进行调用
{
this->m_wndSplitter.SendMessage(WM_SIZE);
}
else
{
m_WndIsValid= true;
}
}
CMySplitter:
void CMySplitter::OnSize(UINT nType,intcx, int cy)
{
CSplitterWnd::OnSize(nType,cx, cy);
if(m_ViewIsValid)//只在视图已经存在的情况下进行调用
{
//当前窗口客户区
CRectrectClient;
GetClientRect(&rectClient);
int width = rectClient.Width();
int height = rectClient.Height();
//保存原视图
CWnd*pOldActiveView = GetActivePane();
// 切分条所在的区域
GetHitRect(201,m_rectTracker);
//移动切分条至客户区中间处
m_rectTracker.OffsetRect((-width/2)*m_split , 0);
// 视图设置
TrackColumnSize(m_rectTracker.left,0);//第0列(左边)设置为m_rectTracker.left宽度
RecalcLayout();
if (pOldActiveView == GetActivePane())
{
if (pOldActiveView != NULL)
{
SetActivePane(-1,-1, pOldActiveView); // re-activate
pOldActiveView->SetFocus();// make sure focus is restored
}
}
return;
}
小结:
用到的方法是不规范的,尤其是CMySplitter类中的函数的重写,实际上他们都已经失去了原来的功能,但是就本工程而言,这种方式是无害的,因为用户不会关心到CSplitterWnd中的这些操作,实际上就是一根切分条的操作。
另外用到的就是主框架类CMainFrame通过发送消息来调用其他类CMySplitter类的方法。