在最近的一个项目中,需要创建一个类似于微软Pocket PC平台上大多数应用程序中的标题栏。然而,不想使用MFC,部分原因是因为它的体积。下面是一个来自Pocket PC 2002的Inbox应用程序的对话框,遮蔽了不感兴趣的区域,以便可以看到主应用程序窗口上的标题栏控件和状态栏控件。
这张图片有两个有趣的特点。第一个特点是顶部的标题控件。它有一个下拉组合框,允许用户在右侧选择排序选项,并在左侧选择相关的文件夹和消息存储。为了保持Windows UI的外观和感觉,如果用户点击下拉,只有下拉部分会被高亮显示。如果用户点击按钮的任何地方,整个按钮都会被高亮显示。
第二个特点是底部的状态栏,它允许用户了解当前文件夹的状态,但稍后会详细介绍。对于标题栏控件,首先想到的是使用标题公共控件来创建图片中的效果。然而,这在用Remote Spy++工作后发现是误导性的。原本认为是标题控件的东西实际上是工具栏控件。
现在,既然已经确定标题栏控件实际上不是标题控件,而是一个工具栏,那么问题就变成了它是如何实现的?在公共控件库中查找后发现,工具栏控件确实支持下拉按钮,但是在实现它们时,组合框下拉按钮的右侧边框看起来不美观,以及左侧边框和文本之间的间隙。
解决第二个问题相对简单,当添加工具栏按钮时,想要显示的不是空白位图,而是什么都不显示。为了纠正这一点,需要指定要使用的位图是I_IMAGENONE(相当于-2),而不是-1,这意味着只显示一个空的位图。
第一个问题更加棘手。如何从组合框中移除分隔线?最初,认为需要完全重写这部分的绘制代码才能让它工作。幸运的是,在公共控件的头文件中有一个未记录的定义CDRF_NOVERTBAR。使用这个作为自定义绘制阶段的一部分,当工具栏控件被重绘时,允许移除分隔条。
上面的代码现在修正了标题栏控件,移除了分隔线和空白图标。据所知,这段代码现在反映了标准Windows应用程序中的控件功能。
以下是WM_NOTIFY处理程序的NM_CUSTOMDRAW函数的代码片段。注意如何告诉公共控件模块的通知处理程序,每当工具栏中的一个项目需要重绘时,每个项目都需要重绘。然后,当一个项目即将被重绘时,返回CDRF_NOVERTBAR代码,通知公共控件模块当前正在绘制的项目应该在没有垂直分隔条的情况下绘制。当然,也可以通过检查NMCUSTOMDRAW结构的dwItemSpec或lItemlParam部分,逐项进行此操作。
case NM_CUSTOMDRAW:
{
LPNMCUSTOMDRAW lpnmcd = (LPNMCUSTOMDRAW)lParam;
switch (lpnmcd->dwDrawStage)
{
case CDDS_PREPAINT:
return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT:
return CDRF_NOVERTBAR;
}
}
最后一部分是处理工具栏按钮被点击时的命令。工具栏按钮由两部分组成,因为按钮是下拉样式按钮。当按下下拉时,需要模拟相应的按钮被按下。为此,需要处理TBN_DROPDOWN通知命令,并根据选择的命令向主窗口发布WM_COMMAND消息。当与按钮关联的命令被点击时,消息会被路由到父窗口,在那里它会被处理在WM_COMMAND处理程序中,就像如果按下工具栏按钮一样。
作为提供的示例的一部分,它加载了一个菜单资源并使用TrackPopupMenu显示它。也可以构建并显示一个树形视图或类似的。实际上,包含在zip文件中的示例源代码确实为左侧下拉按钮做了这件事,但在这里没有详细说明代码。
处理状态栏要简单得多,因为创建状态栏时,栏成功创建但不会显示在屏幕上。这个问题在于,控件被设置为与屏幕底部对齐,而不是菜单栏的顶部,所以当在屏幕底部创建菜单栏时,它实际上遮挡了状态栏,所以状态栏看起来是隐形的,但实际上是隐藏在菜单栏后面的。
要解决这个问题,只需在创建控件时使用CCS_NOPARENTALIGN窗口样式,这样控件就不会与任何客户区域对齐。当然,然后需要手动重新对齐周围的控件,以适应状态栏在菜单栏和主窗口上其余控件之间的状态栏。还会看到,当状态栏最初显示时,它有一个边框和默认文本,这是从窗口创建时开始的。只是使用SB_SETTEXT消息在窗口中设置文本。
在Win32示例中的代码只是一个基本的Pocket PC 2002应用程序,具有"Hello World"的默认实现。这被扩展为在窗口顶部创建一个新的工具栏窗口。要了解如何创建菜单栏控件,请查看名为CreateHeaderBar的函数。
这里需要注意的主要点是,工具栏窗口已经使用TBSTYLE_CUSTOMERASE、TBSTYLE_FLAT和TBSTYLE_LIST创建。最重要的是TBSTYLE_CUSTOMERASE。这将允许拦截并替换新的自定义绘制处理程序绘制阶段,允许移除下拉按钮上的垂直条。
一旦工具栏成功创建,就可以向控件添加两个按钮。实际上有三个按钮,中间一个的宽度计算为在减去左侧和右侧按钮的大小后剩余的宽度,只是一个不可按压的标准按钮。这段代码在辅助函数CreateHeaderRowButtons中。该函数允许一个左侧和/或右侧按钮,但至少必须有一个。传递TBBUTTON结构的原因是它允许程序员最充分地配置按钮的外观和感觉。
需要注意的一个重要点是,在获取按钮的宽度时,需要调用TB_GETITEMRECT而不是TB_GETBUTTONFINO,因为这会得到自动调整大小的按钮的正确宽度,而TB_GETBUTTONINFO则不会。
状态栏的使用更多的是一个疏忽,而不是其他什么。它真的很容易纠正,只需要将样式从隐含的CCS_ALIGNBOTTOM更改为CCS_NOPARENTALIGN,然后允许程序员控制状态栏在屏幕上的大小和位置。
下拉控件的处理是在WM_NOTIFY命令处理程序中的TBN_DROPDOWN子命令中完成的。这只是发布与按钮关联的命令的WM_COMMAND消息。