前言:
void CSplitterWnd::GetColumnInfo(int col, int& cxCur, int& cxMin) ;在使用拆分窗口时我的需要是想调整窗口大小在一定范围 不允许过小和隐藏,通过文档得知此代码设置窗口的当前宽度和最小宽度,但是在实际使用发现在调整窗口小于设定的cxMin时,竟然让窗口消失了,这完全与我的需求背道而驰,因此通过分析源码找到了 较为完美的解决方案,并记录笔记,欢迎朋友们交流互相学习。
注:CSplitterWndEx是继承的CSplitterWnd 只是分割符更加好看了 没有其它拓展功能。
只能点击 【添加类(C)...】 手动继承CSplitterWndEx, 头文件如下设计 重载两个重要的 方法
#pragma once
#include "afxsplitterwndex.h"
class CUserSplitter :
public CSplitterWndEx
{
public:
CUserSplitter();
virtual ~CUserSplitter();
virtual void TrackRowSize(int y, int row);
virtual void TrackColumnSize(int x, int col);
DECLARE_MESSAGE_MAP()
};
其中TrackRowSize 是根据名称便知是调整行的尺寸的,源码中实现方法如下:
//路径 ..\atlmfc\src\mfc\winsplit.cpp 877行
void CSplitterWnd::TrackRowSize(int y, int row)
{
ASSERT_VALID(this);
ASSERT(m_nRows > 1);
CPoint pt(0, y);
ClientToScreen(&pt);
GetPane(row, 0)->ScreenToClient(&pt);
m_pRowInfo[row].nIdealSize = pt.y; // new size
if (pt.y < m_pRowInfo[row].nMinSize)
{//这里的判断是说如果当前行的新Size小于 设定的nMinSize 则移除这一行
// resized too small
m_pRowInfo[row].nIdealSize = 0; // make it go away
if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteRow(row);
}
else if (m_pRowInfo[row].nCurSize + m_pRowInfo[row+1].nCurSize
< pt.y + m_pRowInfo[row+1].nMinSize)
{//这个判断是说如果当前区域的下一行小于最小的nMinSize则移除下一行
// not enough room for other pane
if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteRow(row + 1);
}
}
根据它的判断方式我们便可以最小的改动去实现 限定窗口在一定范围内的缩放
首先重载如下
#include "UserSplitter.h"
CUserSplitter::CUserSplitter()
{
}
CUserSplitter::~CUserSplitter()
{
}
void CUserSplitter::TrackRowSize(int y, int row)
{
ASSERT_VALID(this);
ASSERT(m_nRows > 1);
CPoint pt(0, y);
ClientToScreen(&pt);
GetPane(row, 0)->ScreenToClient(&pt);
m_pRowInfo[row].nIdealSize = pt.y; // new size
if (pt.y < m_pRowInfo[row].nMinSize)
{
// resized too small
m_pRowInfo[row].nIdealSize = m_pRowInfo[row].nMinSize; // 如果小于最小 则设为最小
/*if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteRow(row);*/
}
else if (m_pRowInfo[row].nCurSize + m_pRowInfo[row + 1].nCurSize
< pt.y + m_pRowInfo[row + 1].nMinSize)
{//如果挤压旁边太小 则重新设定大小
// not enough room for other pane
/*if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteRow(row + 1);*/
m_pColInfo[row].nIdealSize = m_pColInfo[row].nCurSize + m_pColInfo[row + 1].nCurSize- m_pColInfo[row + 1].nMinSize;
}
}
void CUserSplitter::TrackColumnSize(int x, int col)
{
ASSERT_VALID(this);
ASSERT(m_nCols > 1);
CPoint pt(x, 0);
ClientToScreen(&pt);
GetPane(0, col)->ScreenToClient(&pt);
m_pColInfo[col].nIdealSize = pt.x; // new size
if (pt.x < m_pColInfo[col].nMinSize)
{
// resized too small
m_pColInfo[col].nIdealSize = m_pColInfo[col].nMinSize; // make it go away
/*if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteColumn(col);*/
}
else if (m_pColInfo[col].nCurSize + m_pColInfo[col + 1].nCurSize
< pt.x + m_pColInfo[col + 1].nMinSize)
{
// not enough room for other pane
/*if (GetStyle() & SPLS_DYNAMIC_SPLIT)
DeleteColumn(col + 1);*/
m_pColInfo[col].nIdealSize = m_pColInfo[col].nCurSize + m_pColInfo[col + 1].nCurSize- m_pColInfo[col + 1].nMinSize;
}
}
从上面代码可知,要限定大小则需要m_pColInfo 数组的数据
则使用时可以做如下设计
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// TODO: 在此添加专用代码和/或调用基类
int rec = m_SplitWnd.CreateStatic(this, 1, 2);
m_SplitWnd.CreateView(0, 0, RUNTIME_CLASS(CUserTreeView), CSize(100, 100), pContext);
m_SplitWnd.CreateView(0, 1, RUNTIME_CLASS(CUserTabView), CSize(100, 100), pContext);
return rec;
//return CFrameWndEx::OnCreateClient(lpcs, pContext);
}
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
CFrameWndEx::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
m_SplitWnd.SetColumnInfo(0, static_cast<int>(cx*0.2), static_cast<int>(cx*0.1));
m_SplitWnd.SetColumnInfo(1, static_cast<int>(cx*0.8), static_cast<int>(cx*0.6));
m_SplitWnd.RecalcLayout();
}