在Visual C++ 版本6中工具条的新特色

owen
微软在www.microsoft.com/visualc已经推出Visual C++6.0预览版几个月了。正式版预计到
今年年底发布。同时,预览版显示出版本6将包含大量的改进和提高,包括支持
Internet控件,例如扁平工具条等。虽然改进的控件包与Internet无关,但它首先出现在
Internet Explorer中,因此它就被取做这个名字了。事实上,官方发布的预览版的标题
是“针对Internet Explorer 4.0的Visual C++ 5.0技术预览”。
在以前关于MFC工具条类的讨论专题中,我曾答应提供一个在版本6中工具条的外观演
示。有一个很好的消息,那就是你现在用CToolBar所作的所有工作在新的版本中都是有
效的,包括那些在以前的栏目中所描述的一些扩展功能。因此,你将很容易修改现存
的程序以获得象Internet Explorer和Visual Studio中那样“酷”的界面。此外,并没有什么
坏消息。
工具条的新特色
早在版本4中,CToolBar就已被MFC库完全实现了。一旦公用控件动态链接库(命名为
comctl32.dll)变得无所不在了,CToolBar就成了如今已包含在操作系统中的工具条控件
的代名词了。然而,CToolBar并没有揭示公用工具条控件的所有能力。如今,通过
CreateEx()函数,它成功了。
公用控件动态链接库现在包含了至少三类风格:最初的、在Internet Explorer 3.0中加入
的以及在Internet Explorer 4.0中加入的。虽然这些版本理论上是向下兼容的,但某些专
业人员曾写出一些不能在后来版本中正常运行的应用程序,这可能是这些程序采用了
一些没有公开的功能,而这些功能并没有被包含在所有的版本中。
Visual C++程序员没有这样的经历,因为在Visual C++4.0或5.0中comctl32.dll并不是一个
可以再分发的组件,它在安装Internet Explorer时被更新,因此MFC程序员无法依靠最新
版本的某些功能来用于他们的程序。这就是CToolBar仅仅具有最初的DLL的有限功能的
原因。CToolBar能够实现最新的特色意味着微软将在Visual C++6.0中包含最新的DLL并将
其作为一个可以再分发的组件。
绝大多数新特色将由在调用CreateEx()和其它CToolBar成员函数时指定的新的风格标志
来确定。下面是commctrl.h的一部分,它定义了TBSTYLE类标识符:
#define TBSTYLE_BUTTON 0x0000
#define TBSTYLE_SEP 0x0001
#define TBSTYLE_CHECK 0x0002
#define TBSTYLE_GROUP 0x0004
#define TBSTYLE_CHECKGROUP (TBSTYLE_GROUP | TBSTYLE_CHECK)
#if (_WIN32_IE >= 0x0300)
#define TBSTYLE_DROPDOWN 0x0008
#endif
#if (_WIN32_IE >= 0x0400)
#define TBSTYLE_AUTOSIZE 0x0010
#define TBSTYLE_NOPREFIX 0x0020
#endif
#define TBSTYLE_TOOLTIPS 0x0100
#define TBSTYLE_WRAPABLE 0x0200
#define TBSTYLE_ALTDRAG 0x0400
#if (_WIN32_IE >= 0x0300)
#define TBSTYLE_FLAT 0x0800
#define TBSTYLE_LIST 0x1000
#define TBSTYLE_CUSTOMERASE 0x2000
#endif
#if (_WIN32_IE >= 0x0400)
#define TBSTYLE_REGISTERDROP 0x4000
#define TBSTYLE_TRANSPARENT 0x8000
#define TBSTYLE_EX_DRAWDDARROWS 0x00000001
#endif
你会注意到其中的一些采用了条件编译,依赖于_WIN32_IE的值,它缺省指的是
Internet Explorer 4.0(即取值为0x0400)。对于Internet Explorer 3.0(即取值为0x0300)
以前的版本,大多数的TBSTYLE标识符指的是按钮或是一组按钮。Internet Explorer 3.0引
入了扁平钮、文本标签、下拉列表和自定义绘制。Internet Explorer 4.0增强了下拉列表
和自定义绘制功能,并且增加了支持OLE拖动目标到一个工具条。
扁平钮和把手
在过去的18个月中我常常被问及该如何获得象Internet Explorer和Visual Studio中的工具
条一样不使用浮雕按钮而是用扁平钮并且带有便于移动和定位的把手那样酷的界面。
这些特色并不被MFC所支持,因此最简单获取的方法就是购买一个扩展库。而对于
Visual C++ 6.0来说却无须多此一举,因为它使得CToolBar类实现了对扁平钮、把手和其
它新的视觉效果的支持。
在预览版中,AppWizard并不会自动包括这些新特色,但它们却很容易被加入。表1显
示了AppWizard创建的主框架窗口的OnCreate()函数,表2显示了需要做哪些修改以获得
具有扁平钮和把手的工具条。图1显示了表1创建出的工具条,而图2显示出了表2实现
的工具条。
表 1: CMainFrame::OnCreate as generated by AppWizard
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if(!m_wndToolBar.Create(this) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if(!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators
图1
表2: Adding flat buttons and the gripper
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if(!m_wndToolBar.CreateEx(this) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if(!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// TODO: Remove this if you don't want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_GRIPPER | CBRS_BORDER_3D | CBRS_TOOLTIPS | CBRS_FLYBY |
CBRS_SIZE_DYNAMIC);
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
图2
为了作出扁平按钮我必须使用CreateEx()来代替Create()。这个新的函数在afxext.h中声
明:
BOOL CreateEx
(
CWnd* pParentWnd, // parent window
DWORD dwCtrlStyle = TBSTYLE_FLAT, // extended style
DWORD dwStyle = // style
WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP,
CRect rcBorders = CRect(0,0,0,0), // border rectangle
UINT nID = AFX_IDW_TOOLBAR // identifier
);
因为扩展风格缺省指的就是TBSTYLE_FLAT,因此我要得到扁平按钮就只需要简单地将
AppWizard形成的代码中的Create()改为CreateEx()即可。我将在后面实现其它的扩展风
格。
为了获得把手,我必须在调用SetBarStyle()函数时包含CBRS_GRIPPER标志,参看表2。
这是CControlBar类的一个新风格,而CToolBar类是从它继承而来的。 请注意到我也加入
了CBRS_BORDER_3D标志,这是为了修正一个未知的绘制问题,该问题将会在工具条的
边缘绘制一些多余的点。这也许意味着预览版确实有这个问题,因为一旦我将3D标志
加入就立即解决了并且也似乎没有影响到别的什么。
上面所作的两个简单的改变是使得一个已存程序获得酷界面的最省力的方法。在一个
程序具有了扁平钮和把手的同时,它也不会发生不应有的其它改变。
文本标签
Internet Explorer使得普通的工具条具有了大按钮和取代了文本提示的文本标签。MFC程
序员可以通过SetButtonText()函数为每个按钮设置一个文本串来获得这种效果。虽然在
Visual C++ 5.0中已包含了这个函数,但如果不使用扁平钮风格则不会取得令人满意的
效果。
表3显示了如何使用现有的文本提示作为按钮的标签,而且图3和图4显示了将工具条分
别定位在顶端和右边的效果。我仍然使得文本提示有效,但你可以通过在调用
SetBarStyle()时去掉CBRS_TOOLTIPS风格而使之无效。
表 3: Adding text labels
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if(!m_wndToolBar.CreateEx(this) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if(!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// TODO: Remove this if you don't want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_GRIPPER | CBRS_BORDER_3D | CBRS_TOOLTIPS | CBRS_FLYBY |
CBRS_SIZE_DYNAMIC);
// Add text to each button
for(int i = 0; i < m_wndToolBar.GetCount(); i++)
{ UINT id = m_wndToolBar.GetItemID(i);
CString s;
if(!s.LoadString(id)) continue;
int j = s.Find(_T('\n'));
if(j < 0) continue;
s = s.Right(s.GetLength() - j - 1);
m_wndToolBar.SetButtonText(i,s); }// Adjust sizes to include text
CRect rect;
m_wndToolBar.GetItemRect(0,&rect);
m_wndToolBar.SetSizes(rect.Size(),CSize(16,15));// TODO: Delete these three lines if you
don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;}
图4
为了产生按钮标签,一个简单的for循环扫过所有的按钮并且从与之相连的帮助串中提
取出提示文本。在设置完每个按钮的标签后,就调用工具条的SetSizes()函数去重新计
算工具条的外观以便使得标签可见。也许会有更好的方法去实现最后一步,但我还是
从微软的MFCIE范例程序中借用了这个过程,不为别的,只为它确实有效。
从很多方面来说,MFCIE都是值得仔细学习的。从本质上来说,它实际上是Internet
Explorer的一个微型版本,并且它还阐述了如何使用版本6的一些新特色。你将会惊讶
于在你的MFC应用程序中添加HTML浏览功能是如此之简单。
在绝大多数情况下你会希望文本标签显示在按钮下面(这在最初的公用控件库中是唯
一的选择)。Internet Explorer 3.0版加入了TBSTYLE_LIST风格,该风格导致文本标签显
示在按钮右边。对于在标签旁边显示一个下拉列表或者按钮被一个子窗口覆盖时,该
风格是很有用的。
下拉列表
在“工具条的变形”一文中,我演示了通过将一个组合框作为一个子窗口来在工具条上
添加下拉列表的情形。该方法在版本6中仍然有效,并且当你想在工具条上显示当前的
可选择项时它仍然是有用的。然而,有时你希望工具条包含一个可以在鼠标点击时显
示选择列表或菜单的按钮。该功能现在可以通过扩展风格TBSTYLE_DROPDOWN和
TBSTYLE_EX_DRAWDDARROWS实现了。
例如,假设我希望对应于“文件”菜单下“新建”的按钮可以显示一个我的应用程序知道如
何去创建的所有文件类型的列表。表4 显示在前面的例子中如何加入该功能,而图5显
示该工具条。我抓取了鼠标停留在该按钮上时的屏幕,它显示出了按钮和下拉箭头之
间的分隔线。
为了加入一个下拉箭头,首先我必须在基本的工具条控件上调用SetExtendedStyle()函
数,并且指定TBSTYLE_EX_DRAWDDARROWS风格。然后我必须在每一个我想要显示下拉
箭头的按钮上指定TBSTYLE_DROPDOWN风格。
表 4: Converting the File menu New command to a drop-down button
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if(!m_wndToolBar.CreateEx(this) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if(!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// TODO: Remove this if you don't want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_GRIPPER | CBRS_BORDER_3D | CBRS_TOOLTIPS | CBRS_FLYBY |
CBRS_SIZE_DYNAMIC);
// Add text to each button
for(int i = 0; i
图5
当用户在New按钮上点击时,就象图5所示一样,基本的按钮操作就发生了。在这个例
子中命令消息导致调用CWinApp::OnFileNew()。然而,当用户点击下拉箭头时,工具条
就发出TBN_DROPDOWN通知消息给工具条的父窗口。我可以通过重载OnNotify()函数或
者通过在消息映射中加入一个ON_NOTIFY而获取这个消息。
表5显示了后者的实现技术。通常我显示一个弹出菜单或者一个列表框去响应这个通知
消息。在绝大多数情况下没有必要提供一个结果值,因为缺省值(TBDDRET_DEFAULT)
显示了我已获取了对这个事件的控制。其它可能的结果值是TBDDRET_NODEFAULT和
TBDDRET_TREATPRESSED。前者表示该事件没有受到控制,当出现后者时,工具条就表
现得好像该按钮被点击一样。
表 5: Handling drop-down notifications
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
//}}AFX_MSG_MAP
ON_NOTIFY(TBN_DROPDOWN,AFX_IDW_TOOLBAR,OnDropDown)
END_MESSAGE_MAP()
void CMainFrame::OnDropDown(NMHDR* pNotifyStruct,LRESULT* result)
{
NMTOOLBAR* pInfo = (NMTOOLBAR*)pNotifyStruct;
switch(pInfo->iItem)
{
case ID_FILE_NEW:
TRACE0("ID_FILE_NEW drop down\n");
// TODO: Display popup menu or list box
break;
// TODO: Add cases for other drop-down buttons
}
}
热点图像
运行Internet Explorer 4.0并且将鼠标在工具条上划过。就能注意到在平时每个按钮是平
淡的颜色。而当鼠标触及到该按钮时,他会凸出来并且显现出鲜艳的颜色。这种视觉
效果采用了工具条控件的被称作“热点图像”的功能。
一般的AppWizard代码使用工具条资源来指定其上按键的外观和与每个按键相连接的一
个命令ID。工具条资源实际上包含一个复合的位图,该位图在调用LoadToolBar()函数时
将被转变成一个图像列表(参见表4)。要激活热点图像功能,我必须通过
SetHotImageList()函数提供第二个图像列表,就象表6所示一样。
表 6: Using hot images
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if(!m_wndToolBar.CreateEx(this) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
CImageList img;
if(!img.Create(IDB_MAINFRAME,16,0,RGB(128,128,128)))
{
TRACE0("Failed to load hot images\n");
return -1;
}
m_wndToolBar.GetToolBarCtrl().SetHotImageList(&img);
img.Detach();
if(!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// TODO: Remove this if you don't want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_GRIPPER | CBRS_BORDER_3D | CBRS_TOOLTIPS | CBRS_FLYBY |
CBRS_SIZE_DYNAMIC);
// Add text to each button
for(int i = 0; i
图6
图6 显示了热点图像被激活的情形。鼠标正停留在New按钮上,它就被显示成一个空文
档的图形。其它的按钮是扁平的和淡颜色的。为了生成这个改进的工具条,我将res子
目录下的toolbar.bmp拷贝为toolbar1.bmp,然后将其作为一个位图资源插入资源中,并
取其ID为IDR_MAINFRAME。 接下来我编辑IDR_MAINFRAME 工具条,用很淡的颜色(例如
淡灰色)来取代它原来的颜色。最后,我加入了如表6所示的对函数SetHotImageList()
的调用。
这个例子演示了在一个已存的工具条中加入热点图像的捷径,但他不会改进工具条的
外观。如果你想在这一点上取得更好的效果,MFCIE的例子包含了非常好的蚀刻位图,
它演示了当你使用非常有艺术性的图像时它能达到何等地步的视觉效果。
其它特色
前面的讲解演示了你怎样简单地改变现存的MFC程序,以获得扁平钮、把手、下拉列
表和热点图像的功能。Visual C++ 6.0还提供了几种其它令人感兴趣的工具条特色,包
括自定义绘制、OLE拖入目标和使用rebar(抱歉,不知道该单词指的是什么)容器
等。这些特色将会是将来的栏目的主题。

燕云剑士
m.owen(欧文)
诗雨共伊飘尘寰,萍花赴水流他年 http://weibo.com/rainchina/