分类:
2008-10-13 16:06:57
实现类似Excel和Visual C++里文件夹式样的标签控制(三)
——实例应用之功能扩充
编译/
在本文的前面和中,我们描述了CFolderTabCtrl标签控制的设计思想、创建过程以及工作原理,通过一个实用程序分析了将CFolderTabCtrl与MFC框架结构融于一体的思路以及关键技巧。CFolderTabCtrl的主要目的是仿真Excel和Visual C++应用程序中标签控制页的UI功能。在这一部分我们将进一步增强和完善CFolderTabCtrl标签控制的仿真效果以及可重用性。内容包括创建多个标签页、并增加让标签页左右滚动的箭头按钮,这两个按钮上分别是示意左右的小三角形。
我们将以里一篇关于图像显示的文章中所附带的源代码作为例子,将CFolderTabCtrl实现的标签控制功能应用到图像显示程序中。原来的程序是一个MFC程序,它通过一个C++类(CPicture)封装Windows系统提供的IPicture 低级COM接口,使我们能轻松地显示各种格式图像文件,包括*.gif、*.jpg、*.bmp和DIBs文件。有关图像显示的具体细节不是本文要讨论的内容,具体细节请参考文章——“”。
首先,我们来看看如何实现箭头按钮?我的基本思路是将按钮作为CFolderTabCtrl的自绘窗口来创建,将标签放置在按钮的右边。如图一所示:
图一 程序中有13个标签页
为实现这个按钮,我创建了一个新类,CFolderButton。它个类是一个自绘按钮类,它有一个DrawItem函数负责绘制按钮,而不是用位图按钮。我选择用GDI类绘制表示左右的三角形,这样的话就不用担心由于缩放而导致的边缘显示问题。CFolderButton::DrawItem自己能绘制三角形来适应按钮的大小。按钮在置灰状态时用3D阴影颜色表示,但按钮被按下时,用象素替换的方法表示按钮状态。CFolderButton还处理鼠标消息以实现两个专用的特性,通常,按钮时不响应双击操作的,但这个按钮可以处理双击鼠标事件,它使得标签滚动两页。也就是说双击相当于两次单击一样。下面是实现代码:
void CFolderButton::OnLButtonDblClk (UINT nFlags, CPoint pt) { SendMessage(WM_LBUTTONDOWN, nFlags, MAKELONG(pt.x,pt.y)); }另一个特性是用户按着按钮不放,则标签会一直滚动,直到标签的端口。实现这个特性的方法是用一个定时器,当定时器被激活时,CFolderButton向它的父窗口发送一个WM_COMMAND消息,就好像按钮已经被压下一样。
void CFolderButton::OnTimer(UINT nIDEvent) { GetParent()->SendMessage(WM_COMMAND, GetDlgCtrlID()); }详细的实现细节请参考本文的源代码。实际上,CFolderButton::OnTimer的实现是有一个启动延时的,所以持续滚动特性犹如键盘操作一样:在重复按下之前有轻微的延时。
// 在 CFolderTabCtrl::OnCreate 中 if (m_dwFtabStyle & FTS_BUTTONS) { CRect rc; for (int id=FTBPREV; id<=FTBNEXT; id++) { VERIFY(m_wndButton[id-1].Create( WS_VISIBLE|WS_CHILD, this, rc, id)); } m_cxButtons = 2*CXBUTTON; }FTS_BUTTONS是CFolderTabCtrl显示按钮的新式样。FTBPREV和FTBNEXT是枚举类型,其值分别为1和2,用于标示按钮的IDs。
// x origin = (按钮的宽度) - (第一个标签页的x 坐标); int xOrigin = m_cxButtons - GetTab(m_iFirstTab)->GetRect().left; dc.SetViewportOrg(xOrigin,0);设置完这个视图窗口后,你不用修改原来的代码便能正确切换标签。其中的转换发生在GDI内部的底层。
BEGIN_MESSAGE_MAP(CFolderTabCtrl, CWnd) …… ON_BN_CLICKED(FTBNEXT,OnNextTab) END_MESSAGE_MAP() void CFolderTabCtrl::OnNextTab() { if (m_iFirstTab < m_lsTabs.GetCount()-1) { m_iFirstTab++; Invalidate(); UpdateButtons(); } }CFolderTabCtrl递增m_iFirstTab并重画。同时调用UpdateButtons函数更新按钮的状态(Enabled)。如果第一个标签可见,也就是左边不会再有标签,UpdateButtons便置灰左边按钮;如果最后一个标签完全可见,也就是右边不会再有标签,UpdateButtons便置灰右边按钮。其实,标签除了占据屏幕空间外不做任何事情。实现细节请参考源代码。(完)