[翻译]-Windows CE 程序设计 (3rd 版)--5.2 公共控件(五)

来源:互联网 发布:telnet 常用端口号 编辑:程序博客网 时间:2024/05/16 13:59
                                                                              翻译:tellmenow

命令带(Command Bands)
命令带控件是一种rebar控件,默认会在控件的每个带区中包含一个命令条。rebar控件是一种控件容器,用户可以在应用程序的窗口上拖动它。命令带只不过是在rebar中的命令条,所以在学习如何对命令带控件进行编程时,知道如何对命令条进行编程就成为最多学习的了。

命令带控件上每个单独的带区都有一个“小夹子”,用于将该带区拖动到新的位置。命令带可以最小化,此时只显示“小夹子”和一个图标;也可以最大化,此时可以覆盖同一行上的其它带区;也可以设置为“还原(Restore)”状态,此时和同一行上的其它带区共享屏幕空间。甚至可以将命令带移动到一个新行,来创建一个多行命令带。图5-4中的窗口顶部,显示了一个两行命令带控件。
图5-4略
命令带的标准用途是将菜单、按钮等命令条上的元素分隔成独立的命令带。这可以让用户按自己的喜好来重新安排这些元素。也可以将独立的命令带显露或者叠在一起,好为菜单、按钮等控件提供更大的空间。

创建命令带控件
创建命令带是很简单的,当然,同创建命令条控件比还是有点复杂。通过调用以下函数来创建命令带控件:
HWND CommandBands_Create (HINSTANCE hinst, HWND hwndParent, UINT wID, DWORD dwStyles, HIMAGELIST himl);
dwStyles参数接受许多标志,用来定义命令带控件的外观和操作行为。这些风格同rebar的很类似,毕竟命令带控件和rebar控件关系密切。

RBS_AUTOSIZE 如果控件尺寸或者位置发生变化,命令带自动调整布局
RBS_BANDBORDERS 每个带上绘制线条来分隔相邻的带
--------------------------------------------------------------------------------------------------------------------
用于命令带控件的图象列表
前面我提到过图象列表。命令条和工具条都在内部使用图象列表,用于管理用在按钮上的图象。可以使用标准的图象列表控件来管理图象列表。该控件基本上就是一个帮助控件,用来协助应用程序管理一系列尺寸相同的图象。Windows CE下的图象列表控件同Windows2000及Windows Me下的是一致的,除非是不支持光标的WindowsCE版本。对于命令带控件来说,只需要创建图象列表,并加入代表最小化状态时的单独命令带的图象集即可。下面展示了一小段相关的代码:
himl = ImageList_Create (16,16,ILC_COLOR,2,0);
hBmp = LoadBitmap (hInst, TEXT("CmdBarBmps"));
DeleteObject (hBmp);
函数ImageList_Create 头两个参数是要装载的图象的尺寸,第三个是图象的格式(ILC_COLOR是默认的),第四个是图象列表里的初始图象数量,最后一个是要添加的图象个数。通过装载一个包含两个图象的双倍宽度的位图以及调用ImageList_Add,可以将两个图象加进来。位图被装进图象列表后,应该删除位图。


RBS_FIXEDORDER 允许移动命令带,但顺序保持不变

RBS_SMARTLABELS 当最小化时,用图标来代表命令带。当命令带还原或者最大化时,会显示标签文本RBS_VARHEIGHT 控件中的每行会根据该行带区的高度调整到最小尺寸高度。当没有该标志的时候,每行的高度按控件中最高的带来计算。
CCS_VERT 创建一个垂直命令带控件
RBS_VERTICALGRIPPER 为垂直命令条显示一个用于移动的“小夹子”。该标志会被忽略,除非设置了CCS_VERT标志
这些风格当中,RBS_SMARTLABELS和RBS_VARHEIGHT是使用最多的两个标志。

RBS_SMARTLABELS标志使用户可以为命令带控件选择有吸引力的外观而不需要应用程序做什么工作。如果在带区使用控件而不是在默认的命令条上,那么RBS_VARHEIGHT标志就是很重要的。

CCS_VERT风格标志会创建一个垂直的命令带控件,但因为WindowsCE不支持垂直菜单,所以带有菜单的命令带在垂直带中是不会正确显示的。不过您可以当控件是垂直方向的时候,隐藏一个特殊的命令带。

增加带区                                                      翻译:tellmenow
REBARBANDINFO结构用来描述控件中的每个带区,通过给CommamndBands_AddBands函数传递一个REBARBANDINFO结构数组,可以给应用程序添加带区。CommandBands_AddBands函数原型如下:
BOOL CommandBands_AddBands (HWND hWndCmdBands, HINSTANCE hinst, UINT cBands, LPREBARBANDINFO prbbi);
再调用该函数之前,您必须先把准备加到控件里的每个带区的信息填写到REBARBANDINFO结构中。该结构定义如下:
typedef struct tagREBARBANDINFO{
    UINT cbSize;
    UINT fMask;
    UINT fStyle;
    COLORREF clrFore;
    COLORREF clrBack;
    LPTSTR lpText;
    UINT cch;
    int iImage;
    HWND hwndChild;
    UINT cxMinChild;
    UINT cyMinChild;
    UINT cyMinChild;
    UINT cx;
    HBITMAP hbmBack;
    UINT wID;
    UINT cyChild;
    UINT cyMaxChild;
    UINT cyIntegral;
    UINT cxIdeal;
    LPARAM lParam;
} REBARBANDINFO;
幸运地是,虽然该结构看上去很宏伟壮观,但其中的许多域可以被忽略掉,因为对未初始化的域有默认值。和通常的Windows结构一样,做为一种容错安全措施,cbSize需要填充为结构的尺寸。fMask域则填充了许多标志,用来指出结构中其它域中哪些域填写的是有效信息。在讨论到每个域的时候,我会描述这些标志的。

如果fMask域中指定了RBBIM_STYLE标志,那fStyle域就必须使用带区的风格标志来填充。适用的标志如下:
RBBS_BREAK 带区从新的一行开始
RBBS_FIXEDSIZE 带区不能被调整大小。当指定了该标志,带区的“小把手”将不显示。
RBBS_HIDDEN 当命令带被创建后,带区不可视。
RBBS_GRIPPERALWAYS 带区有可调尺寸的小把手,即使命令带只有一个带区。
RBBS_NOGRIPPER 带区没有可调尺寸的小把手,因此用户不能移动带区
RBBS_NOVERT 如果使用CCS_VERT风格,命令带控件垂直显示的话,带区不会显示出来。
RBBS_CHILDEDGE 在带区的顶部和底部绘制边线
RBBS_FIXEDBMP 当带区改变大小的时候,带区的背景位图不移动。
基本上这些标志是自解释型的。虽然命令带通常显示在窗口顶部,但是他们可以创建成垂直命令带并显示在窗口左边。在这种情况下,RBBS_NOVERT风格允许程序员来指定当命令带在垂直方向时,带区不显示。包含菜单或略宽一些控件的带区是很适合这种标志的,因为它们在垂直带区上不能被正确显示。

clrFore和clrBack域中填写的是颜色信息,在应用程序绘制带区的时候,会用这些颜色绘制前景色和背景色。需要注意的是,只有在掩码域中设置了RBBIM_COLORS标志时这两个域才有用。对这两个域以及用来为带区指定背景位图的hbmBack域来说,只有在带区包含透明命令条的时候才有用。否则命令条会占据带区的大部分空间,并遮盖任何背景位图或者颜色。在“配置单独带区”一节中,我将解释如何使命令条透明。

lpText域指定了用来标记单个带区的可选文本。这些文字将直接显示在带区条的左边。iImage域用于指定一个显示在带区条左边的位图,使用包含在图像列表控件中的图象列表的索引来填充iImage域。当和命令带控件的RBS_SMARTLABELS风格配套使用的时候,文字和位图显地更加重要。指定了RBS_SMARTLABELS风格以后,如果带区被还原或者最大化,文字就会显示出来;如果带区被最小化,位图就显示出来。该技术被H/PC Explorer用在它的命令带控件上。

wID域中填充的是带区的ID值。带区ID是很重要的,如果您计划在创建带区后配置它们或者想查询它们的状态的话。即使您不打算在程序里使用带区ID,保持每个带区ID唯一也是很重要的,因为控件自身使用该ID来管理带区。只有在fMask域中设置了RBBIM_ID标志时才会检查wID域。

如果带区中默认的命令条控件需要被另一个控件替代的话,hwndChild域就是很有用的了。为了替换命令条控件,必须首先创建新控件,再把该控件的窗口句柄放到hwndChild域中。只有在fMask域中设置了RBBIM_CHILD标志时才会检查hwndChild域。

cxMinChild和cyMinChild域中定义了带区可以缩小到的最小尺寸。当使用默认命令条以外的控件时,这两个域对定义带区的高度和最小宽度很有用。只有在fMask域中设置了RBBIM_CHILDSIZE标志时才会检查这两个域。

当带区被用户最大化的时候,会使用cxIdeal 域。如果这个域没有初始化,命令带最大化时会伸展到整个控件宽度。通过设置cxIdeal,应用程序可以限制带区最大化的宽度,这对于带区上的控件只占整个带区宽度一部分的情况来说是很方便的。只有在fMask域中设置了RBBIM_IDEALSIZE标志时才会检查这个域。

lParam域提供了一个空间来存储由应用程序定义的关于带区信息的值。只有在fMask域中设置了RBBIM_LPARAM标志时才会检查这个域。

REBARBANDINFO中的其它域多用于更灵活的rebar控件,而不仅仅是命令带控件。下面的代码创建了一个命令带控件,初始化了一个带三个REBARBANINFO结构的数组并把带区加到控件里。
// Create a command bands control.
hwndCB = CommandBands_Create (hInst, hWnd, IDC_CMDBAND, RBS_SMARTLABELS | RBS_VARHEIGHT, himl);
  
// Initialize common REBARBANDINFO structure fields.
for (i = 0; i < dim(rbi); i++) {
    rbi[i].cbSize = sizeof (REBARBANDINFO);
    rbi[i].fMask = RBBIM_ID | RBBIM_IMAGE | RBBIM_SIZE | RBBIM_STYLE;
    rbi[i].fStyle = RBBS_FIXEDBMP;
    rbi[i].wID = IDB_CMDBAND+i;
}
// Initialize REBARBANDINFO structure for each band.
// 1. Menu band.
rbi[0].fStyle |= RBBS_NOGRIPPER;
rbi[0].cx = 130;
rbi[0].iImage = 0;
  
// 2. Standard button band.
rbi[1].fMask |= RBBIM_TEXT;
rbi[1].cx = 200;
rbi[1].iImage = 1;
rbi[1].lpText = TEXT ("Std Btns");
  
// 3. Edit control band.
hwndChild = CreateWindow (TEXT ("edit"), TEXT ("edit ctl"),
                          WS_VISIBLE | WS_CHILD | WS_BORDER,
                          0, 0, 10, 5, hWnd, (HMENU)IDC_EDITCTL,
                          hInst, NULL);
  
rbi[2].fMask |= RBBIM_TEXT | RBBIM_STYLE | RBBIM_CHILDSIZE | RBBIM_CHILD;
rbi[2].fStyle |= RBBS_CHILDEDGE;
rbi[2].hwndChild = hwndChild;
rbi[2].cxMinChild = 0;
rbi[2].cyMinChild = 25;
rbi[2].cyChild = 55;
rbi[2].cx = 130;
rbi[2].iImage = 2;
rbi[2].lpText = TEXT ("Edit field");
  
// Add bands.
CommandBands_AddBands (hwndCB, hInst, 3, rbi);
上面的代码中创建的命令带控件有三个带区,一个包含一个菜单,一个包含一组按钮,一个则使用编辑控件代替了命令条。使用RBS_SMARTLABELS 和 RBS_VARHEIGHT 风格创建了控件。当命令条最小化的时候,智能标签显示一个图标;当不在最小化状态时,显示文字图标。RBS_VARHEIGHT风格则允许控件上的每行有不同的高度。

在一个循环里初始化了REBARBANINFO结构中共用的域。接下来,结构中剩余的域根就据控件中的每个带区进行定制。包含编辑控件的第三个带区初始化是最复杂的。该带区需要更多的初始化,因为编辑控件需要适当的调整尺寸来匹配其它带区中命令条控件的标准高度。

使用图像列表索引来初始化每个带区中的iImage域。该图像列表创建并传递进了CommandBands_Create函数。用在第二和第三个带区中的文本域被标记所填充。包含菜单的第一个带区中并不包含文本标记,因为菜单不需要标记。可以对第一个带区使用RBBS_NOGRIPPER风格,这样它就不能在控件上移动了。这可以使菜单带区固定显示在控件中适当的位置上。

既然我们已经创建了带区,那是时候来看看如何初始化它们了。(待续)

配置单独带区                                                                                                翻译:tellmenow

进行到这里时,命令带控件已经创建,单独单区已经加到控件中了。接下来我们有更多的任务要做,就是去配置每个带区中单独的命令条控件。(实际上,配置命令条控件比起前面讲述的命令条要略微复杂一些。)

可以使用下面的函数来获取带区中的命令条句柄:
HWND CommandBands_GetCommandBar (HWND hwndCmdBands, UINT uBand);
uBnad是包含该命令条的带区的基于0的索引。当命令带控件被初始化时调用该函数的话,索引值直接同带区加到控件的顺序相关联。然而,一旦用户有机会拖拽带区到一个新的顺序,那您的应用程序必须通过发送RB_IDTOINDEX消息给命令带控件,以获取索引值,如下所示:
nIndex = SendMessage (hwndCmdBands, RB_INTOINDEX, ID_BAND,0);

这个消息对管理带区是很重要的,因为许多函数和消息都需要使用带区索引来识别带区。问题在于索引值是不固定的,因为用户移动带区导致索引值变化。不要期望索引值是连贯的。作为一个规则,在没有用RB_IDTOINDEX查询索引值之前,不要盲目使用索引值。

一旦您获得命令条窗口句柄,使用标准的命令条控件函数和消息,就可以很简单地把菜单或者按钮加到命令条中了。大部分情况下,在第一个命令条中只加入菜单,在第二个中只加入按钮,将其它控件加到第三个及后续命令条中。

下面的代码完成了前面提到的创建过程。首先初始化了头两个带区中的命令条控件。因为第三个带区有编辑控件,所以不需要初始化该带区。最后一行代码是调用CommandBands_AddAdornments函数将关闭按钮加到控件中。
// Add menu to first band.
hwndBand = CommandBands_GetCommandBar (hwndCB, 0);
CommandBar_InsertMenubar (hwndBand, hInst, ID_MENU, 0);
  
// Add standard buttons to second band.
hwndBand = CommandBands_GetCommandBar (hwndCB, 1);
CommandBar_AddBitmap (hwndBand, HINST_COMMCTRL, IDB_STD_SMALL_COLOR, 15, 0, 0);
CommandBar_AddButtons (hwndBand, dim(tbCBStdBtns), tbCBStdBtns);
  
// Add exit button to command band.
CommandBands_AddAdornments (hwndCB, hInst, 0, NULL);

保存带区布局                                             翻译:tellmenow

命令带控件的可配置能力给程序员带来一个问题。用户重新排列带区后,希望定制的布局在应用程序下一次启动的时候能够恢复。通过使用下面的函数,可以轻易的达到目的:
BOOL CommandBans_GetRestoreInformation (HWND hwndCmdBands, UINT uBand, LPCOMMANDBANDSRESTOREINFO pcbr);
该函数将单个带区的信息保存到COMMANDBANDSRESTOREINFO结构中。该函数有两个参数,一个是命令带控件句柄,一个是将要查询的带区的索引值。下面的代码片段展示了如何查询命令带控件中每个带区的信息。
// Get the handle of the command bands control.
hwndCB = GetDlgItem (hWnd, IDC_CMDBAND);
  
// Get information for each band.
for (i = 0; i < NUMBANDS; i++) {
    // Get band index from ID value.
    nBand = SendMessage (hwndCB, RB_IDTOINDEX, IDB_CMDBAND+i, 0);
  
    // Initialize the size field, and get the restore information.
    cbr[i].cbSize = sizeof (COMMANDBANDSRESTOREINFO);
    CommandBands_GetRestoreInformation (hwndCB, nBand, &cbr[i]);
}

上面的代码使用RB_IDTOINDEX消息,将带区ID转化成带区索引,以用于CommandBands_GetRestoreInformation。结构中的数据通常存储在系统注册表中。我将在第8章“文件和注册表”中讲解如何读写注册表数据。

当应用程序恢复的时候,应该从注册表中读取恢复信息,并在创建命令带控件的时候使用。
// Restore configuration to a command band.
COMMANDBANDSRESTOREINFO cbr[NUMBANDS];
REBARBANDINFO rbi;
  
// Initialize size field.
rbi.cbSize = sizeof (REBARBANDINFO);
  
// Set only style and size fields.
rbi.fMask = RBBIM_STYLE | RBBIM_SIZE;
  
// Set the size and style for all bands.
for (i = 0; i < NUMBANDS; i++) {
    rbi.cx = cbr[i].cxRestored;
    rbi.fStyle = cbr[i].fStyle;
  
    nBand = SendMessage (hwndCB, RB_IDTOINDEX, cbr[i].wID, 0);
    SendMessage (hwndCB, RB_SETBANDINFO, nBand, (LPARAM)&rbi);
}
  
// Only after the size is set for all bands can the bands
// needing maximizing be maximized.
for (i = 0; i < NUMBANDS; i++) {
    if (cbr[i].fMaximized) {
        nBand = SendMessage (hwndCB, RB_IDTOINDEX, cbr[i].wID, 0);
        SendMessage (hwndCB, RB_MAXIMIZEBAND, nBand, TRUE);
    }
}

上面的代码假设命令带控件已经按照默认配置创建。在实际应用中,尺寸和风格的恢复信息用于最初创建控件的时候。在那种情况下,剩余的将根据COMMANDBANDSRESTOREINFO结构中fMaximized域的状态来决定是否最大化带区。只有在所有的带区被创建和适当的调整尺寸后,才做上面代码中的最后一步。

该系统对保存和恢复带区的布局有一个限制,就是您没有办法判断带区在控件中的顺序。带区索引不能提供可靠的信息,因为经过用户几次重新安排带区后,索引值既不连续也不是按任何定义的顺序。解决该问题的唯一方式是强制排列带区,这样用户就不能重新排列带区。通过设置RBS_FIXEDORDER风格,可以达到这个目的。这样可以解决这个问题,但如果用户想要一个不同的顺序,就不能够给用户提供帮助了。在本节最后的例子程序中,我使用带区索引值来猜测顺序。但这个方法不能保证一定能有效。