翻譯 tellmenow
Windows CE的基本目標定位--小型個人生產力工具--在驅動著公用控制項的需求。議程和任務管理應用程式中頻繁用到時間和日期的需求導致在控制項中包括了日期和時間選擇控制項以及日曆控制項。個人生產力工具的小螢幕促成了節省空間的命令條。命令條控制項和用於IE3.0的rebar控制項結合產生了命令帶控制項。命令帶控制項為位於Windows CE應用程式頂部的菜單、按鈕和其它控制項提供了更多的空間。
最後是Pocket PC開發人員所熟悉的菜單控制項。在Windows CE.NET 4.2中,它被加到了Windows CE公用控制項中。我在討論完命令條控制項和命令帶控制項後,會談到菜單控制項。
命令條
簡單地講,命令條控制項將菜單和工具條組合到了一起。這個組合是很有價值的,因為菜單和工具條組合到一行中可以節省空間的受限的Windows CE顯示器的螢幕空間。對程式員來說,命令條就像一個帶有很多協助函數的工具條,使公用控制項編程變的輕而易舉。當您使用命令條的時候,除了可以使用命令條函數外,您還可以使用許多工具條訊息。圖5-1中展示了一個帶有命令條的視窗。
圖5-1(略):帶有命令條的視窗。
建立命令條
您可以通過幾步操作來建立命令條,每步都由一個特別的函數來定義。先建立命令條,再加入菜單、按鈕、其它控制項以及工具提示,最後,添加關閉和協助按鈕到命令條的右側。
通過調用CommandBar_Create函數來開始建立命令條,該函數原型如下:
HWND CommandBar_Create (HINSTANCE hInst, HWND hwndParent, int idCmdBar);
函數需要程式的執行個體控制代碼,父視窗控制代碼以及控制項的ID。如果建立成功,函數返回新建立的命令條控制項的控制代碼。對應用程式來說,光禿禿的命令條並沒有多少用處,還需要加入菜單和一些按鈕來使它變的有用。
命令條菜單
您可以通過調用下面兩個函數之一來給命令條添加菜單。第一個函數是
BOOL CommandBar_InsertMenubar (HWND hwndCB, HINSTANCE hInst, WORD idMenu, int iButton);
頭兩個參數是命令條控制代碼和應用程式的執行個體控制代碼。idMenu是要被裝載到命令條的菜單的資源ID。最後一個參數是緊靠菜單左邊的按鈕的索引。因為Windows CE規範中指定菜單應該再命令條的左端,所以該參數應該設定為0,表示所有按鈕都在菜單右邊。
CommandBar_InsertMenubar函數的缺點是它要求從資源中載入菜單。您不能在運行時配置菜單。當然,通過裝載一個虛擬菜單,並使用不同的菜單函數來操作菜單的內容,是可能達到這個目的的,但這裡有一個更簡單的方法:
BOOL CommandBar_InsertMenubarEx (HWND hwndCB, HINSTANCE hInst, LPTSTR pszMenu, int iButton);
函數CommandBar_InsertMenubarEx中,除了第三個參數pszMenu外,其它參數都和CommandBar_InsertMenubar 中的類似。該參數要麼是菜單資源的名字,要麼是程式先前建立的菜單的控制代碼。如果pszMenu是菜單控制代碼,那麼hInst參數應該是NULL。
一旦菜單被裝載進命令條,就可以在任何時候通過如下函數來被檢索菜單控制代碼:
HMENU CommandBar_GetMenu (HWND hwndCB, int iButton);
第二個參數iButton是緊靠菜單左側的按鈕的索引。這種機制提供了在命令條上識別多個菜單的能力。然而,根據給出的WINDOWS CE設計規範,使用者在命令條上只能看到一個菜單。有了菜單控制代碼,您就可以有許多可用的菜單函數來操縱菜單的結構了。
如果應用程式要修改命令條上的菜單,應用程式必須調用
BOOL CommandBar_DrawMenuBar(HWND hwndCB,int iButton);
它將強迫重繪命令條上的菜單。這裡的參數又一次使用了命令條的控制代碼和菜單左側按鈕的索引。在WINDOWS CE中,您必須使用CommandBar_DrawMenuBar,而不是用在其它的WINDOWS版本中重繪菜單的標準函數DrawMenuBar。
命令條按鈕
給命令條增加按鈕的過程需要兩步,這和給工具條增加按鈕的過程類似。首先必須給命令條增加用於按鈕的位元影像圖片。其次增加按鈕,每個按鈕對應先前加入的位元影像列表裡的一個圖片。
命令條在一個內部圖片列表中維護按鈕的位元影像列表。位元影像可以一次一個的增加到圖象列表中或者將一組圖象包含在一個長而且狹窄的位元影像中。例如,對一個包含了4個16*16像素圖象的位元影像來說,其尺寸將是64*16像素。圖5-2顯示了給位元影像圖象的布局。
圖5-2:包含4個16*16像素圖象的位元影像
裝載一個位元影像可以通過下面的函數來完成。
int CommandBar_AddBitmap (HWND hWndCB, HINSTANCE hInst, int idBitmap, int iNumImage, int iImageWidth, int iImageHeight);
頭兩個參數和以往的命令條函數一樣,是命令條控制代碼和可執行程式的執行個體控制代碼。第三個參數,idBitmap,是位元影像圖象的資源ID。第四個參數iNumImages是載入的位元影像中圖象的數量。只要需要,可以多次調用CommandBar_AddBitmap將多張位元影像載入到同一個命令條中。最後兩個參數是圖象的尺寸,都設定為16。
有兩個預定義的位元影像提供了許多通常用在命令條和工具條中的圖象。通過將CommandBar_AddBitmap中的hInst設定為HINST_COMMCTRL,將idBitmap設定為IDB_STD_SMALL_COLOR或IDB_VIEW_SMALL_COLOR,您可以裝載這兩個位元影像。圖5-3展示了這兩個位元影像中包含的圖象。第一行的按鈕包含了來自標準位元影像的圖象,而第二行按鈕包含了標準視窗中的位元影像。
圖5-3:由公用控制項DLL提供的兩個標準位元影像中的圖象。
這些圖象的索引值定義在CommCtrl.h中,所以您不需要知道它們在位元影像中的確切順序。這些常量定義如下:
訪問標準位元影像的常量定義
STD_CUT Edit/Cut button image
STD_COPY Edit/Copy button image
STD_PASTE Edit/Paste button image
STD_UNDO Edit/Undo button image
STD_REDOW Edit/Redo button image
STD_DELETE Edit/Delete button image
STD_FILENEW File/New button image
STD_FILEOPEN File/Open button image
STD_FILESAVE File/Save button image
STD_PRINTPRE Print preview button image
STD_PROPERTIES Properties button image
STD_HELP Help button (Use Commandbar_Addadornments function to add a help button to the command bar.)
STD_FIND Find button image
STD_REPLACE Replace button image
STD_PRINT Print button image
訪問標準視窗中位元影像的常量定義
VIEW_LARGEICONS View/Large Icons button image
VIEW_SMALLICONS View/Small Icons button image
VIEW_LIST View/List button image
VIEW_DETAILS View/Details button image
VIEW_SORTNAME Sort by name button image
VIEW_SORTSIZE Sort by size button image
VIEW_SORTDATE Sort by date button image
VIEW_SORTTYPE Sort by type button image
VIEW_PARENTFOLDER Go to Parent folder button image
VIEW_NETCONNECT Connect network drive button image
VIEW_NETDISCONNECT Disconnect network drive button image
VIEW_NEWFOLDER Create new folder button image
載入到命令條中的圖象是按它們在圖象列表中的索引進行引用的。例如,如果載入的位元影像包含5個圖象,要引用位元影像中第四個圖象,則其基於0的索引值是3。
如果通過多次調用CommandBar_AddBitmap將多個位元影像圖象集加到命令條中的話,將前面的圖象數加象在當前列表中的索引,即為當前圖象的引用值。例如,通過兩次調用CommandBar_AddBitmap加入了兩個圖象集,其中第一次加入的是5個圖象,第二次加入的是4個圖象,那麼要引用第二個圖象集中的第三個圖象,需要用第一個圖象裡的圖象總數(5)加上對第2個位元影像的索引(2),得到引用為7。
一旦載入了位元影像,就可以通過下面兩個函數之一來載入按鈕。
第一個函數是:
BOOL CommandBar_AddButtons ( HWND hWndCB, UINT uNumButtons, LPTBBUTTON lpButtons);
CommandBar_AddButtons可以一次就給命令條增加一系列按鈕。該函數傳入一個按鈕數量和一個指向TBBUTTON結構數組的指標。數組中的每個元素描述一個按鈕。TBBUTTON結構定義如下:
typedef struct {
int iBitmap;
int idCommand;
BYTE fsState;
BYTE fsStyle;
DWORD dwData;
int iString;
} TBBUTTON;
iBitmap設定為按鈕使用的位元影像圖象。也就是我剛解釋的圖象列表中基於0的索引。第二個參數是按鈕的命令ID。當使用者點擊按鈕的時候,通過WM_COMMAND訊息,該ID被發送到父視窗。
fsState域則定義了按鈕的初始狀態。該域所允許的值如下:
TBSTATE_ENABLED 按鈕有效。如果沒有指定該標誌,按鈕將失效並變灰。
TBSTATE_HIDDEN 按鈕在命令條上不可見。
TBSTATE_PRESSED 按鈕顯示為下壓狀態。
TBSTATE_CHECKED 按鈕初始被選中。只有當按鈕是TBSTYLE_CHECKED風格時該狀態才可用。
TBSTATE_INDETERMINATE 按鈕變灰。
最後一個標誌TBSTATE_WRAP,儘管其在文檔中有定義,但在命令條中它是無效的。該標誌用在工具條超過一行而折行時。
fsStyle指定了按鈕的初始風格,用來表明按鈕如何響應動作。按鈕可以定義為標準下壓按鈕,複選框按鈕,下拉式清單方塊,以及類似單選框的複選框,此時只允許一組中的一個按鈕被選擇。對fsStyle來說可能的標誌如下:
TBSTYLE_BUTTON 標準下壓按鈕
TBSTYLE_CHECK 複選框,每次使用者點該按鈕時,在選擇和取消選擇之間切換。
TBSTYLE_GROUP 定義一組按鈕的開始按鈕。
TBSTYLE_CHECKGROUP 表示是一組複選框中的一個,並且行為和單選框類似,每次只能有一個按鈕被選擇。
TBSTYLE_DROPDOWN 下拉式清單方塊。
TBSTYLE_AUTOSIZE 按鈕尺寸取決於按鈕的文本。
TBSTYLE_SEP 定義了一個分隔條,在按鈕之間插入一小塊空間。
TBBUTTON結構中的dwData域裡是應用程式定義的值。通過使用TB_SETBUTTONINFO和TB_GETBUTTONINFO訊息,應用程式可以設定和查詢該值。iString則定義了在包含按鈕文字的命令條字元數組中的索引。該域也可以添充為指向按鈕文本的字串的指標。
給命令條增加按鈕的另一個函數如下:
BOOL CommanBar_InsertButton (HWND hwndCB, int iButton, LPTBBUTTON lpButton);
該函數將一個按鈕插到命令條中iButton所對應的按鈕的左邊。除了指向單個TBBUTTON結構的lpButton外,該函數的參數同CommandBar_AddButtons類似。iButton給出了新按鈕在命令條上的位置。
使用命令條按鈕
除了下拉按鈕外,當使用者按一個命令條按鈕時,命令條都會給其父視窗發送一個WM_COMMAND訊息。所以處理命令條上的按鈕點擊就像處理功能表命令一樣。實際上,因為命令條上許多按鈕都有等價的功能表命令,所以習慣上為按鈕和同功能的菜單使用相同的命令ID號,這樣可以減少對命令條按鈕進行特定的處理的需求。
命令條會維護單個複選框和一組複選框按鈕的選擇狀態。當按鈕被加到命令條中以後,可以使用下面兩個訊息來查詢或者設定它們的狀態:TB_ISBUTTONCHECKED和TB_CHECKBUTTON。(首碼TB_暗示了命令條和工具條控制項之間的密切關係。)把要查詢的按鈕的ID放到參數wParam中,將TB_ISBUTTONCHECKED訊息按下面的方式發送出去:
fChecked = SendMessage (hwndCB, TB_ISBUTTONCHECKED, wID, 0);
hwndCB是包含按鈕的命令條的控制代碼。該函數如果傳回值非零,表示按鈕被選擇了。要將按鈕設定成選擇狀態,可以給命令條發送一個TB_CHECKBUTTON訊息,方法如下:
SendMessage (hwndCB, TB_CHECKBUTTON, wID,TRUE);
要取消選擇,只要將TRUE換成FALSE即可。
使按鈕失效
Windows CE允許您很容易的修改命令條或者工具條上失效按鈕的外觀。命令條和工具條維護兩個圖象列表:先前講述過的標準圖象列表和一個失效圖象列表,該列表用來儲存用於失效按鈕的圖象。
要使用該特性,您需要為失效按鈕建立和裝載第2個圖象列表。而最容易的方式就是用談論CommandBar_AddBitmap時描述的技術,為按鈕的普通狀態建立一個圖象列表。(可以使用TB_LOADIMAGES訊息來裝載工具條中的圖象列表。)一旦完成了圖象列表,只要簡單的複製原始圖象列表,並修改圖象列表中的位元影像,為原始位元影像建立一個失效副本即可。接下來將新的圖象列表裝載回命令條或者工具條即可。下面的程式碼片段展示了如何完成這些工作的:
HBITMAP hBmp, hMask;
HIMAGELIST hilDisabled, hilEnabled;
// Load the bitmap and mask to be used in the disabled image list.
hBmp = LoadBitmap (hInst, TEXT ("DisCross"));
hMask = LoadBitmap (hInst, TEXT ("DisMask"));
// Get the standard image list and copy it.
hilEnabled = (HIMAGELIST)SendMessage (hwndCB, TB_GETIMAGELIST, 0, 0);
hilDisabled = ImageList_Duplicate (hilEnabled);
// Replace one bitmap in the disabled list.
ImageList_Replace (hilDisabled, VIEW_LIST, hBmp, hMask);
// Set the disabled image list.
SendMessage (hwndCB, TB_SETDISABLEDIMAGELIST, 0, (LPARAM) hilDisabled);
代碼中首先裝載了一個位元影像和一個掩碼位元影像,它們將用於替換失效圖象列表中的位元影像。通過給命令條發送TB_GETIMAGELIST訊息,可以獲得當前的圖象列表,之後用ImageList_Duplicate來複製一個圖象列表。最後用之前裝載的位元影像來替換圖象列表中的相應圖象即可。
本例僅替換了一個位元影像,但在實際應用中可以替換許多位元影像。如果所以有的位元影像都被替換,那麼從頭建立一個失效的圖象列表可能比複製一個標準圖象列表再替換其中的一些位元影像會更容易一些。一旦新的圖象列表被建立完成,您可以通過發送TB_SETDISABLEDIMAGELIST訊息將列表裝載進命令條中。上面的代碼對Windows CE下的命令條一樣有效。
下拉按鈕
同命令條上的標準按鈕相比,下拉按鈕顯得更複雜一些。對使用者來說,它就是一個按鈕,當被按下的時候,會顯示一個清單項目來讓使用者選擇。對程式員來說,下拉按鈕實際上就是按鈕和菜單的組合,當使用者點擊按鈕的時候會顯示一個菜單。不幸地是,命令條對下拉按鈕支援有限,只提供了修改按鈕外觀的功能以表示是一個下拉按鈕以及當使用者點擊按鈕時會發送特定通知的功能。另外,是由應用程式決定如何顯示菜單的。
通過發送帶TBN_DROPDOWN通知碼的WM_NOTIFY訊息,使用者點擊下拉按鈕的通知將會發送到命令條的父視窗中。當父視窗接收到TBN_DROPDOWN通知後,它必須立即在通知訊息中標識的下拉按鈕下建立一個快顯功能表。菜單由父視窗使用適當的選項來填充。當有功能表項目被選擇時,菜單將發送WM_COMMAND訊息,表示功能表項目被選擇了並且菜單即將消失。為了更容易理解如何處理下拉按鈕通知,可以瀏覽以下處理TBN_DROPDOWN通知的過程:
LRESULT DoNotifyMain (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
LPNMHDR pNotifyHeader;
LPNMTOOLBAR pNotifyToolBar;
RECT rect;
TPMPARAMS tpm;
HMENU hMenu;
// Get pointer to notify message header.
pNotifyHeader = (LPNMHDR)lParam;
if (pNotifyHeader->code == TBN_DROPDOWN) {
// Get pointer to toolbar notify structure.
pNotifyToolBar = (LPNMTOOLBAR)lParam;
// Get the rectangle of the drop-down button.
SendMessage (pNotifyHeader->hwndFrom, TB_GETRECT,
pNotifyToolBar->iItem, (LPARAM)&rect);
// Convert rect to screen coordinates. The rect is
// considered here to be an array of 2 POINT structures.
MapWindowPoints (pNotifyHeader->hwndFrom, HWND_DESKTOP,
(LPPOINT)&rect, 2);
// Prevent the menu from covering the button.
tpm.cbSize = sizeof (tpm);
CopyRect (&tpm.rcExclude, &rect);
// Load the menu resource to display under the button.
hMenu = GetSubMenu (LoadMenu (hInst, TEXT ("popmenu")),0);
// Display the menu. This function returns after the
// user makes a selection or dismisses the menu.
TrackPopupMenuEx (hMenu, TPM_LEFTALIGN | TPM_VERTICAL,
rect.left, rect.bottom, hWnd, &tpm);
}
return 0;
}
當代碼判斷出是TBN_DROPDOWN通知後,首先要做的就是擷取下拉按鈕的矩形。擷取該矩形是為了使下拉式功能表能被立即定位到按鈕下方。要達到該目的,程式會發送TB_GETRECT訊息給命令條,其中參數wParam是下拉按鈕的ID,參數lParam是指向矩形結構的指標。
因為返回的矩形是基於父視窗的座標,而快顯功能表是螢幕座標,所以必須進行座標轉換。這可以使用以下函數來完成:
MapWindowPoints (HWND hwndFrom, HWND hwndTo, LPPOINT lppoints, UINT cPoints);
第一個參數是原始座標的視窗控制代碼。第二個參數是將要轉換到的座標所在的視窗控制代碼。第三個參數是指向將被轉換的座標點數組的指標。最後一個參數是數組中點的數量。在我所展示的程式裡,視窗控制代碼分別是命令條控制代碼和桌面視窗控制代碼。
一旦矩形被轉換成案頭座標,就可以建立快顯功能表或者和上下文相關的菜單了。要這樣做,您首先需要從資源中裝載菜單並調用TrackPopupMenuEx來顯示菜單。如果您能回想起前面章節中對TrackPopupMenuEx的討論,那麼就會知道TPMPARAMS結構包含一個不會被菜單顯示所覆蓋的矩形。對次,矩形被設定成下拉按鈕的大小,這樣按鈕就不會快顯功能表所覆蓋了。fuFlags中則包含了許多值,用來定義菜單的位置。對下拉按鈕來說,只需要TPM_VERTICAL標誌。如果TPM_VERTICAL被設定,菜單會儘可能的為非覆蓋矩形保留更多的水平地區。TrackPopupMenuEx函數會一直等到功能表項目被選擇或者當使用者在螢幕的其它地方點擊後菜單消失時才會返回。(待續)