使用本机 C++ 进行 windows vista 开发(4)

新浪科技
欢迎阅读第 1 期《使用 C++ 进行 windows 开发》。我将重点介绍使用本机 C++ 进行 windows vista™ 和 windows server® 2008 开发。本专栏将展现最新版本的 windows® 中引入的令人激动的新功能。
之所以创办此专栏,有两个原因。首先,尽管 microsoft® .NET Framework 取得了惊人的成功,但多数情况下,使用本机 C++ 仍然不失为明智之举。.NET 的应用势头有增无减,而 C++ 仍有其用武之地。其次,一有新的重要操作系统推出,就期待所有不同的应用程序框架(MFC、活动模板库 (ATL)、.NET 以及其他框架)能在短期内将所有新功能和增强功能“一网打尽”是不切实际的。C++ 仍是可不受限使用 windows SDK 各个组件的唯一语言,而 windows vista 以及今后推出的版本中肯定有许多新的领域尚待挖掘!有关 .NET 功能的更多信息,请参阅“关于 .NET”边栏。
作为此系列的开篇之作,我将主要介绍控件方面的一些知识。Windows vista 并未引入过多的新控件(尽管它确实提供了新的网络地址控件,该控件允许输入和验证 IPv4、IPv6 和 DNS 名称),而是为许多现有标准和通用控件提供了增强功能及新功能。从某种意义上讲,这是好消息,因为这表明您只要花一点点精力就可以在自己的应用程序中充分利用许多新功能。在本专栏中,我将带您快速了解一些最通用的控件,并介绍一些在 windows vista 以及今后推出的版本中提供的令人激动的新功能。《MSDN® 杂志》的网站上提供了此专栏的下载,下载内容包括本文介绍的多数控件功能的示例。
本文导航: | ||
·前言 ·按钮控件 ·树视图控件 | ·标头控件和列表视图控件 ·关于 .NET |
按钮控件
按钮控件主要负责 windows vista 中几项看似新增的控制功能。与传统按钮相比,新的样式标记采用了明显不同的外观。图 1 显示了一些新的按钮样式。
图 1 新的 windows vista 按钮样式
BS_SPLITBUTTON 样式可创建拆分按钮。该按钮的父窗口接收指示用户单击了该按钮上的下拉箭头的 BCN_DROPDOWN 通知消息。然后,由用户负责显示快捷菜单。可能按如下所示实现用户的下拉处理程序:
CRect rectangle; VERIFY(m_splitButton.GetWindowRect( &rectangle)); TPMPARAMS params = { sizeof(TPMPARAMS) }; params.rcExclude = rectangle; CMenuHandle menu = m_menu.GetSubMenu(0); VERIFY(menu.TrackPopupMenuEx(TPM_LEFTBUTTON, rectangle.left, rectangle.bottom, m_hWnd, ¶ms));
此代码段首先计算按钮的边界,然后填充 TPMPARAMS 结构以通知 TrackPopupMenuEx;如果菜单无法在预定位置显示,它不应与该按钮重叠,而应显示在下一个可用边缘上。您还可以使用 BCM_GETSPLITINFO 和 BCM_SETSPLITINFO 消息来查询和调整下拉箭头的外观,但多数情况下,默认设置就完全可以满足您的需求。
BS_COMMANDLINK 样式可创建命令链接。命令链接的特点是在按钮的标题旁边显示一个箭头,还会以较小的字体显示可选说明。按钮的标题通常是使用 SetWindowText 函数来设置,而新的 BCM_SETNOTE 消息设置了显示在标题下面的说明。
最后,您可以让 windows 在按钮上显示时下著名的“所需提升”屏蔽,具体方法是:向其发送 BCM_SETSHIELD 消息,将 LPARAM 设置为 TRUE 可包括图标,设置为 FALSE 可删除图标。以下是一个简单示例:
button.SendMessage(BCM_SETSHIELD, 0, required);
编辑
windows xp 引入了使用 EM_SETCUEBANNER 消息在编辑控件上显示文本提示的功能。此文本提示将提示用户注意当控件获得焦点时消失的信息。Windows vista 对此进行了适度而有价值的补充,使您能够在控件获得焦点时依然显示文本提示。这个看似微小的改进使得静态控件在许多情况下不再必要。只须使用以前未使用的 WPARAM,将其设为 TRUE,即可利用此功能。为简便起见,提供了 Edit_SetCueBannerTextFocused 宏:
VERIFY(Edit_SetCueBannerTextFocused(m_edit, L”Cue text”, TRUE));
windows vista 还使用 CB_SETCUEBANNER 消息以大致相同的方式为组合框提供了此功能。但是,Windows vista 不提供当控件具有焦点时,显示文本提示的选项,只有“下拉列表”式组合框会继续显示文本提示,直至做出选择为止,如图 2 所示。
图 2 组合框示例
为简便起见,提供了 ComboBox_SetCueBannerText 宏:
VERIFY(ComboBox_SetCueBannerText(m_comboBox, L”Cue text”));
本文导航: | ||
·前言 ·按钮控件 ·树视图控件 | ·标头控件和列表视图控件 ·关于 .NET |
树视图控件
windows 开发人员经过努力,最终为树视图控件引进了扩展样式。图 3 显示了某些使用中的新的树视图控件样式和主题。
图 3 树视图
提供 TVM_GETEXTENDEDSTYLE 和 TVM_SETEXTENDEDSTYLE 消息以获取和设置扩展样式的位掩码。TreeView_GetExtendedStyle 宏可包含 TVM_GETEXTENDEDSTYLE 消息,该宏的使用方法如下:
DWORD style = TreeView_GetExtendedStyle(m_treeView);
但是,通常您不必获取扩展样式,因为 TVM_SETEXTENDEDSTYLE 消息允许您更新使用位掩码的样式标记子集。例如,利用包含 TVM_SETEXTENDEDSTYLE 消息的 TreeView_SetExtendedStyle 宏,您可以在添加 TVS_EX_AUTOHSCROLL 扩展样式的同时删除 TVS_EX_MULTISELECT 扩展样式,具体代码如下所示:
TreeView_SetExtendedStyle(m_treeView, TVS_EX_AUTOHSCROLL, TVS_EX_AUTOHSCROLL | TVS_EX_MULTISELECT);
毋庸置疑,对树视图控件的改进中,最令人兴奋的莫过于使控件能够支持多种选择。最终,开发人员在进行多种选择时可以抛弃所有代码,而只需使用 TVS_EX_MULTISELECT 扩展样式,让系统来完成其余处理。当然,您会希望枚举所选项目。通过为 TVM_GETNEXTITEM 消息引入新标记,枚举所选项目是可以实现的。首先像以往一样使用 TVGN_CARET 标记获取当前所选项目,然后使用 TVGN_NEXTSELECTED 标记获取剩余的所选项目。下面是使用 windows Template Library CTreeViewCtrlEx 和 CTreeItem 类的一个简单示例:
for (CTreeItem item = m_treeView.GetSelectedItem(); 0 != item; item = item.GetNext(TVGN_NEXTSELECTED)) { CString text; item.GetText(text); TRACE(L”%s\n”, text); }
CTreeViewCtrlEx 的 GetSelectedItem 方法发送带有 TVGN_CARET 标记的 TVM_GETNEXTITEM 消息以获取第一个所选项目,而 CTreeItem 的 GetNext 方法再次发送带有指定标记和树项目句柄的 TVM_GETNEXTITEM 消息来提供消息上下文。
如果您只想知道所选项目的数目,利用 TVM_GETSELECTEDCOUNT 消息实现。关联的 TreeView_GetSelectedCount 宏可包含该消息,该宏的使用方法如下:
DWORD count = TreeView_GetSelectedCount(m_treeView);
还有几个扩展样式值得一试。TVS_EX_DOUBLEBUFFER 扩展样式可“告诉”控件通过双缓冲进行绘制。这样可避免在重新调整控件大小时出现闪烁。TVS_EX_AUTOHSCROLL 扩展样式允许控件自动将所选的树项目显示到视图中。这通常与 TVS_NOHSCROLL 样式结合使用,后者可隐藏水平滚动条。以下是一个简单示例:
DWORD treeViewStyle = m_treeView.GetStyle(); treeViewStyle |= TVS_NOHSCROLL; m_treeView.SetWindowLong(GWL_STYLE, treeViewStyle); TreeView_SetExtendedStyle(m_treeView, TVS_EX_AUTOHSCROLL, TVS_EX_AUTOHSCROLL);
尽管默认行为通常已经足够,但您可以使用 TVM_SETAUTOSCROLLINFO 消息控制自动滚动动画特性。
您还可能已注意到:Windows vista 上的 windows 资源管理器现在支持新的具有淡入淡出效果的“expando”按钮,使用新的箭头位图代替了原有的加/减框。要切换到箭头,您需要使用 SetWindowTheme 函数以指示 windows 对指定控件使用与 windows 资源管理器一致的可视样式:
COM_VERIFY(::SetWindowTheme(m_treeView, L”explorer”, 0));
使用 TVS_EX_FADEINOUTEXPANDOS 扩展样式实现淡入淡出效果。
本文导航: | ||
·前言 ·按钮控件 ·树视图控件 | ·标头控件和列表视图控件 ·关于 .NET |
标头控件和列表视图控件
虽然只有相当少的开发人员直接使用标头控件,但几乎所有 windows 开发人员都熟悉列表视图控件,而列表视图控件在很大程度上依赖于标头控件来实现许多功能。在 windows vista 中,标头控件和列表视图控件都得到了增强。由于大多数开发人员都熟悉列表视图控件,因此,在本节中,我将从列表视图控件的角度着重介绍这些功能。不过,其中的许多功能不借助列表视图控件也可使用,而某些功能需要与标头控件直接交互。
windows vista 中的列表视图控件已全面升级,具有许多新功能、样式和增强功能。与树视图控件不同,用于列表视图控件的扩展样式已推出相当长一段时间了。但是,Windows vista 定义了少数新的扩展样式,大大丰富了用户交互。
当调整某一列的大小时,LVS_EX_AUTOSIZECOLUMNS 扩展样式可自动重新调整视图的列的大小,以适应视图中的各列。这是一个很好的功能,用户会非常喜欢,您可以轻松地添加该扩展样式。
LVS_EX_AUTOCHECKSELECT 样式提供了在 Web 应用程序的表格式控件中常用的那种“全选”(Select All) 复选框。它与新的 HDS_CHECKBOXES 标头控件样式相对应。复选框被置于项目复选框上方的列标头中。单击该复选框可一次选中或取消选中所有列表视图项复选框。图 4 提供了一个与此相关的示例。
图 4 列表视图全选控件
对于树视图控件,要获取列表视图的 windows 资源管理器主题,您只需调用 SetWindowTheme 函数以便让 windows 使用与 windows 资源管理器一致的可视样式。
LVS_EX_COLUMNSNAPPOINTS 扩展样式与 LVCOLUMN 结构添加项结合使用,可在用户重新调整特定列的大小时将该列迅速置于最小列宽。使用 LVS_EX_AUTOSIZECOLUMNS 扩展样式时也会提供此行为。要利用此功能,可添加上述任一扩展样式并相应地准备 LVCOLUMN 结构。例如,在以下示列中,插入了一个初始列宽为 200 像素、最小列宽为 100 像素的列:
LVCOLUMN column = { 0 }; column.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_MINWIDTH; column.pszText = L”Name”; column.cx = 200; // initial width column.cxMin = 100; // minimum width m_listView.InsertColumn(0, &column);
新的 LVCF_MINWIDTH 掩码用于指示 cxMin 字段已填充并表示该列的最小宽度。
您可能已注意到 windows vista 中的另一个功能,即 windows 资源管理器可在所有视图而非仅在详细信息视图模式中显示列标题。默认情况下,标头控件仅在详细信息视图模式中可见。只需使用 LVS_EX_HEADERINALLVIEWS 扩展样式即可在所有视图模式中显示列标题。虽然用户会有点不习惯,但由于此功能允许用户在不同的视图模式下对视图排序,因此堪称一项很有用的功能。
您还可能已经注意到,在不同视图模式下(如平铺视图模式),Windows 资源管理器中的列无法重新调整大小,水平滚动条亦不能用于使任何溢出的列标题显示在视图中。而在标头控件中,可显示溢出按钮。单击该按钮会显示一个弹出菜单,其中会列出因不可见而溢出的标题列。重复此行为需要执行若干步骤。
首先,您需要将视图模式设置为除详细信息视图以外的任何模式,然后,需要使用 LVS_EX_HEADERINALLVIEWS 扩展样式来显示列标题。其次,您需要将 HDS_NOSIZING and HDS_OVERFLOW 样式添加到列表视图控件的标头控件中,具体代码如下所示:
CHeaderCtrl header = m_listView.GetHeader(); DWORD headerStyle = header.GetStyle(); headerStyle |= HDS_NOSIZING | HDS_OVERFLOW; header.SetWindowLong(GWL_STYLE, headerStyle);
HDS_NOSIZING 样式可禁用所有列的大小调整功能,HDS_OVERFLOW 样式可按需显示溢出按钮。最后,您需要处理 HDN_OVERFLOWCLICK 标头控件通知消息。在此步骤中,您可以向用户显示某种弹出窗口或菜单以表示溢出列。您还可能希望使用 TrackPopupMenu 函数来完成此功能。通过通知消息的 LPARAM 提供的 NMHEADER 结构的 iItem 数目为您提供了溢出的第一列的索引。
标头控件提供的另一项新功能是拆分按钮。当鼠标悬停在列标题上时,列标题的外观与有关按钮控件一节中所述的拆分按钮很相似,显示一个下拉按钮。当为新列准备 LVCOLUMN 结构时,请使用 LVCFMT_SPLITBUTTON 格式标记。该格式标记与直接使用标头控件时 HDITEM 结构所使用的 HDF_SPLITBUTTON 格式标记相对应。按下下拉按钮时,LVN_COLUMNDROPDOWN 通知消息由列表视图控件发送。此通知消息与直接使用标头控件时所用的 HDN_DROPDOWN 通知消息相对应。LVN_COLUMNDROPDOWN 消息的 LPARAM 是一个指向 NMLISTVIEW 结构的指针,iSubItem 字段指示对其下拉列表执行了单击操作的列的索引。
在 windows vista 中,列表视图控件的另一个改进方面是分组功能。图 5 显示了某些可应用到组中的新标签。用于定义列表视图组的 LVGROUP 结构规模已成倍增长。
图 5 列表视图分组
可按如下所示创建图 5 中的组:
LVGROUP group = { sizeof(LVGROUP) }; group.mask = LVGF_GROUPID | LVGF_HEADER | LVGF_SUBTITLE | LVGF_TASK | LVGF_FOOTER | LVGF_STATE | LVGF_ALIGN; group.iGroupId = 1; group.pszHeader = L”Header”; group.pszSubtitle = L”Subtitle”; group.pszTask = L”Task”; group.pszFooter = L”Footer”; group.state = LVGS_COLLAPSIBLE; group.uAlign = LVGA_FOOTER_RIGHT | LVGA_HEADER_CENTER; m_listView.InsertGroup(-1, &group);
本文导航: | ||
·前言 ·按钮控件 ·树视图控件 | ·标头控件和列表视图控件 ·关于 .NET |
关于 .NET
无疑,.NET Framework 未来的版本 中将包含许多我在本专栏文章中介绍的功能。然而,您无需等到新的版本推出时,才在您基于 .NET Framework 的应用程序中使用这些功能,因为 windows Forms 提供了多数情况下所必需的“挂钩”,可以用新的功能对其进行扩展。
我所介绍的许多功能都需要向控件发送特定消息。一般使用由 user32.dll 系统库导出的 SendMessage 函数来发送消息,您可以如下所示将其导入您的受管理的应用程序:
[DllImport("user32.dll")] static extern int SendMessage(IntPtr window, int message, IntPtr wParam, IntPtr lParam);
As an example, the section on tree view controls in this column described how to enable multiple selections. 如果 windows Forms 树视图类使用了如下代码,您也可以完成上述功能:
int TVM_SETEXTENDEDSTYLE = 4396; int TVS_EX_MULTISELECT = 2; SendMessage(m_treeView.Handle, TVM_SETEXTENDEDSTYLE, new IntPtr(TVS_EX_MULTISELECT), new IntPtr(TVS_EX_MULTISELECT));
以下示例说明了处理通知消息的方式: windows Forms 控件类提供了受保护的 WndProc 方法,使您能够根据需要处理特定消息。许多由控件发送的旨在通知各种事件的控件拥有者的消息均以 WM_NOTIFY 消息的形式发送至 LPARAM 是一个指向 NMHDR 结构指针的地方。NMHDR 结构的代码成员可识别所发送的特定消息。以下示例显示了如何通过受管理的代码来处理通知消息:
struct NMHDR { public IntPtr hwndFrom; public IntPtr idFrom; public int code; } const int WM_NOTIFY = 0x004E; protected override void WndProc(ref Message message) { bool handled = false; if (WM_NOTIFY == message.Msg) { NMHDR header = (NMHDR)Marshal.PtrToStructure(message.LParam, typeof(NMHDR)); if (<someCode> == header.code) { // TODO: handle notification here handled = true; } } if (!handled) { base.WndProc(ref message); } }
LVGS_COLLAPSIBLE 状态标记允许对组进行折叠和扩展。对组进行折叠将隐藏属于该组的列表视图项。LVGS_COLLAPSED 状态标记可用于以编程方式对组进行折叠。LVGA_ 标记可用于重写组标签的默认对齐方式。页眉和页脚文本通常是左对齐的;副标题总是显示在标题下方。任务链接总是采用右对齐方式。LVN_LINKCLICK 通知消息是在用户单击任务链接时发送的。由作为消息的 LPARAM 提供的 NMLVLINK 指针的 iSubItem 字段识别任务链接所属的组。
最后,您可能还需要响应 LVN_GETEMPTYMARKUP 通知消息以便在列表视图控件没有目时提供某些文本供显示。您可以使用定位标记在文本中创建链接。以下是用于处理通知消息的函数的示例:
LRESULT OnGetEmptyMarkup(LPNMHDR notifyData) { NMLVEMPTYMARKUP* markupInfo = reinterpret_cast<NMLVEMPTYMARKUP*>(notifyData); markupInfo->dwFlags = EMF_CENTERED; wcscpy_s(markupInfo->szMarkup, _countof(markupInfo->szMarkup), L”Link <A>one</A> and <A>two</A>.”); return TRUE; // set the markup }
默认情况下,消息显示在列表视图的左上角。EMF_CENTERED 标记可使消息水平居中显示或垂直居中显示。列表视图报告用户单击一个链接的方式与单击多个组任务链接的方式相同。但是,可以共享特定 LVN_LINKCLICK 通知消息处理程序。标记链接为空时,NMLVLINK 结构的 iItem 和 iSubItem 成员将被设为 -1,嵌套的 LITEM 结构将用设为 EmptyMarkup 的链接标识符填充,其 iLink 成员设为文本内的链接的索引。
结束语
借助 windows SDK,在深入研究 windows vista 控件时,您会发现还有许多其他的小功能和增强功能。许多以前需要所有者或者自定义绘制功能的情况现在都有现成的方法可用。另外,今后推出的 windows Template Library 和 MFC 会具有许多新的改进,使您可以更轻松地利用 windows vista 控件的增强功能。
作者:Kenny Kerr
本文导航: | ||
·前言 ·按钮控件 ·树视图控件 | ·标头控件和列表视图控件 ·关于 .NET |