一、关于CSplitterWnd类
我们在使用CuteFtp或者NetAnt等工具的时候,一般都会被其复杂的界面所吸引,在这些界面中
窗口被分割为若干的区域,真正做到了窗口的任意分割。 那么我们自己如何创建类似的界面,也实现
窗口的任意的分割呢 ?在VC6.0中这就需要使用到CSplitterWnd类。CSplitterWnd看上去像是一种
特殊的框架窗口,每个窗口都被相同的或者不同的视图所填充。当窗口被切分后用户可以使用鼠标移动
切分条来调整窗口的相对尺寸。虽然VC6.0支持从AppWizard中创建分割窗口,但是自动加入的分割条
总是不能让我们满意,因此我们还是通过手工增加代码来熟悉这个类。
CSplitterWnd的构造函数主要包括下面三个。
BOOL Create(CWnd* pParentWnd,int nMaxRows,int nMaxCols,SIZE sizeMin,
CCreateContext* pContext,DWORD dwStyle,UINT nID);
功能描述:该函数用来创建动态切分窗口。 参数含义:pParentWnd 切分窗口的父框架窗口。
nMaxRows,nMaxCols是创建的最大的列数和行数。 sizeMin是窗格的现实大小。 pContext 大多数
情况下传给父窗口。 nID是字窗口的ID号.
BOOL CreateStatic(CWnd* pParentWnd,int nRows,int nCols,DWORD dwStyle,UINT nID)
功能描述:用来创建切分窗口。 参数含义同上。
BOOL CreateView (int row,int col,CruntimeClass* pViewClass,SIZE sizeinit,
CcreateContext* pContext);
功能描述:为静态切分的窗口的网格填充视图。在将视图于切分窗口联系在一起的时候必 须先将切分
窗口创建好。
参数含义:同上。
从CSplitterWnd源程序可以看出不管是使用动态创建Create还是使用静态创建CreateStatic,在函
数中都调用了一个保护函数CreateCommon,从下面的CreateCommon函数中的关键代码可以看出创建
CSplitterWnd的实质是创建了一系列的MDI子窗口。
DWORD dwCreateStyle = dwStyle & ~(WS_HSCROLL|WS_VSCROLL);
if (afxData.bWin4)
dwCreateStyle &= ~WS_BORDER; //create with the same wnd-class as MDI-Frame (no erase bkgnd)
if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle,
0, 0, 0, 0,pParentWnd->m_hWnd, (HMENU)nID, NULL))
return FALSE; // create invisible
二、创建嵌套分割窗口
2.1创建动态分割窗口
动态分割窗口使用Create方法。下面的代码将创建2x2的窗格。
m_wndSplitter.Create(this,2,2,CSize(100,100),pContext);
但是动态创建的分割窗口的窗格数目不能超过2x2,而且对于所有的窗格,都必须共享同一个视图,所受
的限制也比较多,因此我们不将动态创建作为重点。我们的主要精力放在静态分割窗口的创建上。
2.2创建静态分割窗口
与动态创建相比,静态创建的代码要简单许多,而且可以最多创建16x16的窗格。不同的窗格我们可以使
用CreateView填充不同的视图。
在这里我们将创建CuteFtp的窗口分割。CuteFtp的分割情况如下:
CCuteFTPView
CView2 CView3
CView4
创建步骤:
▲ 在创建之前我们必须先用AppWizard生成单文档CuteFTP,生成的视类为 CCuteFTPView.同时在增加
三个视类或者从视类继承而来的派生类CView2,CView3 CView4.
▲ 增加成员:
在Cmainfrm.h中我们将增加下面的代码:
CSplitterWnd wndSplitter1;
CSplitterWnd wndSplitter2;
▲ 重载CMainFrame::OnCreateClient()函数:
BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
{ //创建一个静态分栏窗口,分为三行一列
if(m_wndSplitter1.CreateStatic(this,3,1)==NULL)
return FALSE;
//将CCuteFTPView连接到0行0列窗格上
m_wndSplitter1.CreateView(0,0,RUNTIME_CLASS(CCuteFTPView),CSize(100,100), pContext);
m_wndSplitter1.CreateView(2,0,RUNTIME_CLASS(CView4),CSize(100,100),pContext);
//将CView4连接到0行2列
if(m_wndSplitter2.CreateStatic(&m_wndSplitter,1,2,WS_CHILD|WS_VISIBLE,
m_wndSplitter.IdFromRowCol(1, 0))==NULL)
return FALSE; //将第1行0列再分开1行2列
//将CView2类连接到第二个分栏对象的0行0列
m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CView2),CSize(400,300),pContext);
//将CView3类连接到第二个分栏对象的0行1列
m_wndSplitter2.CreateView(0,1,RUNTIME_CLASS(CView3),CSize(400,300),pContext);
return TRUE;
}
2.3实现各个分割区域的通信
■有文档相连的视图之间的通信
由AppWizard生成的CCuteFTPView是与文档相连的,同时我们也让CView2与文档相连,因此我们需要修改
CCuteFTPApp的InitInstance()函数,我们将增加下面的部分。
AddDocTemplate (new CMultiDocTemplate(IDR_VIEW2TYPE,
RUNTIME_CLASS(CMainDoc),
RUNTIME_CLASS(CMDIChildWnd),
RUNTIME_CLASS(CView2)));
我们现在来实现CCuteFTPView与CView2之间的通信。由于跟文档类相连的视图类 是不能安全的与除文档
类之外的其余的视图类通信的。因此我们只能让他们都与文档 类通信。在文档中我们设置相应的指针以用
来获的各个视图。我们重载 CCuteFTPView::OnOpenDocument()函数。
CCuteFTPView* pCuteFTPView;CView2* pView2;
POSITION pos;
CView* pView;
while(pos!=NULL)
{
pView=GetNextView(pos);
if(pView->IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL)
pCuteFTPView=(CCuteFTPView*)pView;
else(pView->IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL)
pView2=(CView2*)pView;
}
这样我们在文档类中就获的了跟它相连的所有的视图的指针。
如果需要在 CCuteFTPView中调用CView2中的一个方法DoIt()则代码如下:
CCuteFTPDoc* pDoc=GetDocument();CView2* pView2=pDoc->pView3;pView3.DoIt();
■无文档视图与文档关联视图之间的通信
CView3和CView4都是不与文档相关联的。我们现在实现CView3与CView2的通信.正如前面所说,CView2
只能安全的与CCuteFTPDoc通信,因此,CView3如果需要跟CView2通信,也必须借助于文档类。因此程序
的关键是如何在CView3中获得文档的指针。视图类中没有这样的类成员可以用来直接访问文档类。但是我
们知道在主窗口类MainFrame中我们可以获得程序的任意窗口类的指针。因此我们只要获得程序主窗口了
的指针,就可以解决问题了。代码实现在CView3中访问CView2中的DoIt()方法。
CView3中的代码如下:
CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();
CCuteFTPDoc* Doc=(CCuteFTPDoc*)MainFrame->GetActiveDocument();
if(Doc!=NULL) Doc->DoIt();
CCuteFTPDoc中的相应的处理函数DoIt()代码如下:
CView2* pView2;
POSITION pos;
CView* pView;
while(pos!=NULL)
{
pView=GetNextView(pos);
if(pView->IsKindOf(RUNTIME_CLASS(CView2))==NULL)
pView2=(CView2*)pView;
}
pView2->DoIt();
无文档关联视图之间的通信
CView3和CView4都是不跟文档相连的,如何实现他们之间的通信呢。 正如我们在上面所说的那样,由于
在主框架中我们可以访问任意的视图,因此我们的主要任 务还是在程序中获得主框架的指针。在CView3
中访问CView4中的方法DoIt()。
CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();
CView4* View4=(CView4*)MainFrame->m_wndSplitter1.GetPane(2,0);
View4->DoIt();
到现在我们已经实现了CuteFTP的主窗口的框架并且能够实现他们之间相互通信的框架。 同样的我们可以
实现其他的一些流行界面例如NetAnts,Foxmail的分割。
三、关于对话框的分割
到目前为止,只有基于文档/视图的程序才能使用CSplitterWnd,而基于对话框的应用程序却不支持
CSplitterWnd,但是如果我们在继承类中重载一些虚拟方法,也能使CSplitterWnd 在对话框程序中使用。
从MFC的源程序WinSplit.cpp中可以看出,为了获得父窗口的地方程序都调用了虚拟方法GetParentFrame(),
因此如果在对话框中使用,我们必须将它改为GetParent();因此我们将CSplitterWnd的下面几个方法重载。
virtual void StartTracking(int ht);
virtual CWnd* GetActivePane(int* pRow = NULL, int* pCol = NULL);
virtual void SetActivePane( int row, int col, CWnd* pWnd = NULL );
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
virtual BOOL OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult );
virtual BOOL OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult );
具体实现如下,实现中我将给出原有代码的主要部分以及修改后的代码以作对比。
在cpp文件中加入下面的枚举类型。
enum HitTestValue
{
noHit = 0,//表示没有选中任何对象
vSplitterBox = 1,
hSplitterBox = 2,
bothSplitterBox = 3,
vSplitterBar1 = 101,//代表各个方向的水平分割条
vSplitterBar15 = 115,
hSplitterBar1 = 201,//代表垂直方向的各个分割条
hSplitterBar15 = 215,
splitterIntersection1 = 301,//代表各个交叉点
splitterIntersection225 = 525
};
CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol)
{
ASSERT_VALID(this);
//获得当前的获得焦点的窗口
//下面注释粗体的是原有的代码的主要部分。
// CWnd* pView = NULL;
//CFrameWnd* pFrameWnd = GetParentFrame();
//ASSERT_VALID(pFrameWnd);
//pView = pFrameWnd->GetActiveView();
//if (pView == NULL)
// pView = GetFocus();
CWnd* pView = GetFocus();
if (pView != NULL && !IsChildPane(pView, pRow, pCol))
pView = NULL;
return pView;
}
void CxSplitterWnd::SetActivePane( int row, int col, CWnd* pWnd)
{
CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd;
//下面加注释粗体的是原有代码的主要部分。
//FrameWnd* pFrameWnd = GetParentFrame();
//ASSERT_VALID(pFrameWnd);
//pFrameWnd->SetActiveView((CView*)pPane);
pPane->SetFocus();//修改后的语句
}
void CxSplitterWnd::StartTracking(int ht)
{
ASSERT_VALID(this);
if (ht == noHit)
return;
// GetHitRect will restrict ''''m_rectLimit'''' as appropriate
GetInsideRect(m_rectLimit);
if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
{
// split two directions (two tracking rectangles)
int row = (ht - splitterIntersection1) / 15;
int col = (ht - splitterIntersection1) % 15;
GetHitRect(row + vSplitterBar1, m_rectTracker);
int yTrackOffset = m_ptTrackOffset.y;
m_bTracking2 = TRUE;
GetHitRect(col + hSplitterBar1, m_rectTracker2);
m_ptTrackOffset.y = yTrackOffset;
}
else if (ht == bothSplitterBox)
{
// hit on splitter boxes (for keyboard)
GetHitRect(vSplitterBox, m_rectTracker);
int yTrackOffset = m_ptTrackOffset.y;
m_bTracking2 = TRUE;
GetHitRect(hSplitterBox, m_rectTracker2);
m_ptTrackOffset.y = yTrackOffset; // center it
m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2);
m_rectTracker2.OffsetRect(m_rectLimit.Width()/2,
0);
}
else
{
// only hit one bar
GetHitRect(ht, m_rectTracker);
}
//下面加注释的将从程序中删去。
//CView* pView = (CView*)GetActivePane();
//if (pView != NULL && pView->IsKindOf(RUNTIME_CLASS(CView)))
//{
// ASSERT_VALID(pView);
// CFrameWnd* pFrameWnd = GetParentFrame();
//ASSERT_VALID(pFrameWnd);
//pView->OnActivateFrame(WA_INACTIVE, pFrameWnd);
// }
// steal focus and capture
SetCapture();
SetFocus();
// make sure no updates are pending
RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW);
// set tracking state and appropriate cursor
m_bTracking = TRUE;
OnInvertTracker(m_rectTracker);
if (m_bTracking2)
OnInvertTracker(m_rectTracker2);
m_htTrack = ht;
SetSplitCursor(ht);
}
BOOL CxSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
if (CWnd::OnCommand(wParam, lParam))
return TRUE;
//下面粗体的是原程序的语句
//return GetParentFrame()->SendMessage(WM_COMMAND, wParam, lParam);
return GetParent()->SendMessage(WM_COMMAND, wParam, lParam);
}
BOOL CxSplitterWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )
{
if (CWnd::OnNotify(wParam, lParam, pResult))
return TRUE;
//下面粗体的是源程序的语句
//*pResult = GetParentFrame()->SendMessage(WM_NOTIFY,
wParam, lParam);
*pResult = GetParent()->SendMessage(WM_NOTIFY, wParam, lParam);
return TRUE;
}
BOOL CxSplitterWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// The code line below is necessary if using CxSplitterWnd
in a regular dll
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
return CWnd::OnWndMsg(message, wParam, lParam, pResult);
}
这样我们就可以在对话框中使用CxSplitterWnd类了。
四、CSplitterWnd的扩展
CSplitterWnd扩展话题是很多的,我们可以通过对原有方法的覆盖或者增加新的方法来扩展CSplitterWnd。
我们在此仅举两个方面的例子。
4.1锁定切分条
当用户创建好分割窗口后,有时并不希望通过拖动切分条来调节窗口的大小。这时就必须锁定切分条。
锁定切分条的最简单的方法莫过于不让CSplitterWnd来处理WM_LBUTTONDOWN,WM_MOUSEMOVE,WM_SETCURSOR
消息,而是将这些消息交给CWnd窗口进行处理,从而屏蔽掉这些消息。拿WM_LBUTTONDOWN处理过程来说。
修改为如下:
void CXXSplitterWnd::OnLButtonDown(UINT nFlags,CPoint point) {
CWnd::OnLButtonDown(nFlags,point);
}
其余的处理方法类似。
4.2切分条的定制
由Window自己生成的切分条总是固定的,没有任何的变化,我们在使用一些软件比如ACDSee的时候却能发现
它们的切分条却是和自动生成的切分条不一样的。那么如何定制自己的切分条呢?通过重载CSplitterWnd的
虚方法OnDrawSplitter和OnInvertTracker可以达到这样的目的。下面的代码生成的效果是分割窗口的边界
颜色为红色,分割条的颜色为绿色.代码如下:
void CSplitterWndEx::OnDrawSplitter(CDC *pDC, ESplitType nType, const CRect &rectArg)
{
if(pDC==NULL)
{
RedrawWindow(rectArg,NULL,RDW_INVALIDATE|RDW_NOCHILDREN);
return;
}
ASSERT_VALID(pDC);
CRect rc=rectArg;
switch(nType)
{
case splitBorder:
//重画分割窗口边界,使之为红色
pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));
rc.InflateRect(-CX_BORDER,-CY_BORDER);
pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));
return;
case splitBox:
pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
rc.InflateRect(-CX_BORDER,-CY_BORDER);
pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
rc.InflateRect(-CX_BORDER,-CY_BORDER);
pDC->FillSolidRect(rc,RGB(0,0,0));
pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
return;
case splitBar:
//重画分割条,使之为绿色
pDC->FillSolidRect(rc,RGB(255,255,255));
rc.InflateRect(-5,-5);
pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));
return;
default:
ASSERT(FALSE);
}
pDC->FillSolidRect(rc,RGB(0,0,255));
}
void CSplitterWndEx::OnInvertTracker(CRect &rect)
{
ASSERT_VALID(this);
ASSERT(!rect.IsRectEmpty());
ASSERT((GetStyle()&WS_CLIPCHILDREN)==0);
CRect rc=rect;
rc.InflateRect(2,2);
CDC* pDC=GetDC();
CBrush* pBrush=CDC::GetHalftoneBrush();
HBRUSH hOldBrush=NULL;
if(pBrush!=NULL) hOldBrush=(HBRUSH)SelectObject(pDC->m_hDC,pBrush->m_hObject);
pDC->PatBlt(rc.left,rc.top,rc.Width(),rc.Height(),BLACKNESS);
if(hOldBrush!=NULL)
SelectObject(pDC->m_hDC,hOldBrush);
ReleaseDC(pDC);
}
同样我们只要继承CSplitterWnd中的其余的一些虚拟方法就可以生成具有自己个性的分割窗口了。
分享到:
相关推荐
在这个例子中,`splitter2`包含一个水平分割(`splitter1`)和一个垂直分割(`bottom`),从而形成一个两行一列的布局,其中第一行由`topleft`和`topright`组成,可以水平调整,而第二行为`bottom`,可以垂直调整。...
2. 分割方式:大部分音频分割工具允许用户手动设置分割点,也可以通过检测静音段自动分割,确保每首歌曲的完整性。 3. 输出质量:确保分割后的音频文件保持与原文件相同的音质,避免因转码导致音质下降。 4. 软件...
在.NET Framework中,C#开发人员经常使用Splitter控件来创建用户界面,该界面可以动态调整两个或多个区域的大小。Splitter控件在Windows应用程序设计中扮演着重要角色,尤其是在需要用户自定义布局的场景下。本篇将...
// panel2.Width = this.ClientSize.Width - splitter1.SplitDistance; } ``` 以上代码将根据Splitter的位置调整Panel1的宽度。 总结,Splitter和SplitterContainer是C# Windows Forms中用于布局管理和交互的强大...
MP3 Splitter & Joiner Pro v5.1 22 March 2012 released Products MP3 Splitter & Joiner MP3 Splitter & Joiner Pro Audio Recorder Platinum Audio Recorder Pro MIDI MP3 Converter MP3 Audio ...
2. **操作流程**: - **导入视频**:首先,打开Boilsoft Video Splitter软件,点击“添加”按钮,选择需要分割的视频文件。 - **预览与设置切割点**:在预览窗口中,可以使用播放控制条进行快进、快退、暂停等操作...
MP3 Splitter & Joiner Pro4.22Build2Pro版破解补丁v1.1. 修复上一个补丁的一个问题.纯补丁,不含源安装程序.原版(含旧版补丁)可到http://download.csdn.net/source/2127284 下载 并用此处的补丁 MP3 Splitter & ...
2. **多方向分割**:不仅限于水平分割,也支持垂直分割,甚至可以实现双向(水平和垂直)分割,满足多样化的布局需求。 3. **预设比例**:开发者可以预先设定各个区域的默认尺寸比例,确保页面加载时的初始布局。 ...
Boilsoft Video Splitter是一款专为用户设计的强大视频编辑工具,尤其在视频分割领域表现出色。这款7.01.2版本的便携版软件无需安装,只需解压缩后直接运行,大大方便了用户对视频文件的操作。在本文中,我们将深入...
#### 一、Boilsoft Video Splitter 5.16 概述 Boilsoft Video Splitter 是一款功能强大的视频分割软件,能够帮助用户轻松地将大型视频文件分割成多个较小的片段。最新版本 Boilsoft Video Splitter 5.16 支持多种...
VC代码 simple_splitter (实用代码源)VC代码 simple_splitter (实用代码源)VC代码 simple_splitter (实用代码源)VC代码 simple_splitter (实用代码源)VC代码 simple_splitter (实用代码源)VC代码 simple_splitter ...
File Splitter is a useful and free program that enables you to split a large file into small chunks which are easy to be sent and stored, while File Joiner allows you to join these split parts ...
MP3SPLITTER是一款专为音频爱好者设计的MP3文件分割工具,它的主要功能是帮助用户将大的MP3文件按照需求切割成多个小段。在处理MP3音乐、播客或者有声书时,这样的工具非常实用,尤其当你需要提取特定部分或者想将一...
在VC++编程中,"splitter"是一种常用于创建用户界面的组件,它允许用户动态地分割窗口,以便同时查看和操作多个视图。Splitter窗体通常包含两个或更多的子视图,用户可以通过拖动分隔条来调整各视图的大小。这种功能...
2. **API与配置项**: - `splitter()` 函数是库的核心,用于初始化分割布局。可以接受一个配置对象,包含以下参数: - `direction`: 指定分割的方向,如 `'vertical'`(垂直)或 `'horizontal'`(水平)。 - `...
2. **用户界面**:可能对用户界面进行了汉化,使得中国用户更易于理解和操作,提高了用户的友好度。 3. **新增功能**:可能增加了新的规则设置,如基于时间的带宽分配,让用户可以根据一天中的不同时间段来调整带宽...
在这个例子中,当Splitter被移动时,它会改变Panel1和Panel2的宽度,以保持它们的总宽度等于表单的宽度减去Splitter的宽度。 Splitter组件的灵活性还体现在它可以与其他组件如TabControl或PageControl结合使用。...
2. 启动FileSplitter,选择“文件合并”选项。 3. 导入或指定待合并的文件夹路径,程序会自动识别并读取所有分割文件。 4. 开始合并操作,等待进度条完成,合并后的文件将保存在指定位置。 三、操作界面与易用性 ...
Boilsoft Video Splitter 是一个用来分割、剪切或修剪大的 AVI、MPEG、RM、ASF、WMV、3GP 或 MP4 视频文件为小的视频片断而不必重新编码的工具。视频分割软件内置视频播放器,您可以轻易按时间或选择范围来分割 AVI...
Port Splitter 虚拟 串口 wince ce ce6 本工具为虚拟串口PORT-SPLITTER绿色版V2.9,自带的配置文件为比亚迪(BYD)G6的车载导航之用,内有使用说明. 可以方便地用在不能保存注册表的机器上,