分类:
2008-10-13 16:06:57
实现类似Excel和Visual C++里文件夹式样的标签控制(二)
——实例应用
编译/
在我们创建了一个类CFolderTabCtrl,用这个类实现了类似Excel和Visual C++应用中文件夹式样的标签控制。在阅读本文之前,最好先看上一篇文章及其例子代码FldrTab。FldrTab纯粹是一个测试CFolderTabCtrl类的例子,没有什么实质性的用途。为了真正模仿出Excel和Visual C++的文件夹式样标签控制的效果,本文拟将CFolderTabCtrl应用到一个实际的MFC程序中。并且标签的旁边也象Excel一样有水平滚动条,如图一所示:
图一 标签和滚动条
本文的例子原来是一个显示位图(bitmap)文件及其BITMAPINFOHEADER结构信息的程序。图像及其BITMAPINFOHEADER结构信息都是显示在同一个视图画面里。如图二所示:
图二 图像和信息在同一画面
为了将CFolderTabCtrl标签控制类引入到这个程序,我们创建了两个新类,CFolderFrame和CFolderView。此外,我们还要对原来的程序进行改进,使它能够在不同的标签页里分别显示图像和BITMAPINFOHEADER结构信息。如图三和图四:
图三 显示位图图像
图四 显示位图文件格式头结构信息
另外标签控制页中还有一个用于显示原始图像十六进制数据的Hex标签,不过这是一个虚设的标签,我并没有实现它,如果哪位朋友有兴趣,可以自己去完成,做好后别忘了把源代码也给我一份哦!
从个人的角度来讲,我很怀疑用这样的方法来改进程序的可行性,因为我觉得将信息显示在一个画面中会更直观。但是,本文的目的是示范,并不指望拿它去获得UI设计的奖项,仅仅是用它来作为例子,示范如何同时实现CFolderCtrlTab标签控制和滚动条控制,仅此而已。
此外,要记住一点,每当你要修改或增强MFC应用程序框架特性时,尽量少的触及MFC框架本身。模仿它而不要去破坏它。以此为原则,我采取的策略是在框架和视图之间插入新窗口。如图五所示:
图五 插入的新框架与主框架的关系
上图说明了基本的框架关系。主框架(或MDI子框架)包含作为子窗口的CFolderFrame,然后CFolderFrame包含水平滚动条和标签控制并管理它们之间的交互。
CFolderFrame的使用很简单,自己要做的事情并不多。首先必须改写主框架的OnCreateClient函数以便创建CFolderFrame表示的窗口。
BOOL CMainFrame::OnCreateClient(..., CCreateContext* pcc) { return m_wndFolderFrame.Create(this, RUNTIME_CLASS(CDIBView), pcc, IDR_FOLDERTABS); }因为原来的位图显示程序同时支持SDI和MDI版本。对于MDI的情况,你要在MDI子框架中改写OnCreateClient,而OnCreateClient函数通常是MFC创建视图的地方,如今应该在此创建CFolderFrame子框架。不要调用基类的OnCreateClient!然后,CFolderFrame子框架用运行时类创建一个视图和你要传递的上下文信息。IDR_FOLDERTABS是串资源的ID,用它存储标签名。如果你想指定动态的标签名,可以省略这个参数并调用CFolderFrame::GetFolderTabCtrl来获得标签控制,然后用CFolderTabCtrl::AddItem添加标签页。有关细节请参考上一篇文章和附带的源代码。
void CDIBView::OnChangedFolder(int iPage) { m_iPage = iPage; UpdateScrollSizes(); Invalidate(); }记住不要忘了修改视图的OnDraw函数,让它绘制正确的标签页,改进后的程序需要在标签页之间来回切换,m_iPage表示页索引,它的值分别为0,1,2,三个标签页分别用来绘制图像、显示BITMAPINFOHEADER结构信息和显示十六进制数据。最后,你必须在CDIBView::OnInitialUpdate中加一行显示CFolderFrame框架控制的代码:
// 在 CDIBView::OnInitialUpdate 函数中 GetFolderFrame()->ShowControls(pDIB ? CFolderFrame::bestFit : CFolderFrame::hide);CFolderFrame::ShowControls可以让你隐藏和显示标签控制和滚动条。这样当程序为SDI并且启动空框架时——也就是说没有文档/视。这时程序中的pDIB==NULL,CDIBView::OnInitialUpdate传递CFolderFrame::hide来隐藏控制;否则传递CFolderFrame::bestFit来指示CFolderFrame根据需要的宽度显示所有标签,然后用剩下的宽度显示滚动条。如果你想用其它的算法也未尝不可,你可以计算宽度,然后用这个调用CFolderFrame::ShowControls。
CScrollBar* CFolderView::GetScrollBarCtrl(int nBar) const {return GetFolderFrame()->GetScrollBar(nBar);}它将控制传递到CFolderFrame:
CScrollBar* CFolderFrame::GetScrollBar (int nBar) { return nBar==SB_HORZ ? &m_wndSBHorz : nBar==SB_VERT ? &m_wndSBVert : NULL; }这比预料的要简单多了!MFC设计的灵活性真是令人吃惊。要想使用自己创建的与CScrollView一致的滚动条,仅仅改写GetScrollBarCtrl就可以了。例如,你可以发明有一个有迷幻色彩的超级滚动条……哈哈,帅呆了。
void CFolderFrame::OnHScroll(...) { GetView()->SendMessage(WM_HSCROLL, ...); }对于WM_VSCROLL消息也同样如法炮制。一旦你解决的这些鸡毛蒜皮似的问题,滚动条的运行便OK了,它工作起来就像CScrollView一样。