菜單和按鈕
例子:菜單1
本小節僅僅向你展示如果向你的窗口中加入一個基本的菜單,通常你會用到一個提前制作好的菜單資源,這會是一份.rc文件並且會被編譯鏈接進你的.exe可執行程序中。這是具體的流程做法,而商業編譯器將會有一個資源編輯器,你可以通過這個編輯器來創建菜單,但是在這個例子中我會向你展示如何用.rc文件的手動寫法。通常我會配合使用一個頭文件,在資源文件和源文件中我們需要引入這個頭文件,這個頭文件中包含了控制和菜單選項等的標識符。
在本小節的栗子中,你可以按照指示在simple_window代碼的基礎上做改造。
首先頭文件名通常會叫“resource.h”
1 #define IDR_MYMENU 101
2 #define IDI_MYICON 201
3
4 #define ID_FILE_EXIT 9001
5 #define ID_STUFF_GO 9002
沒有更多的東西,我們的菜單很簡單,標識符名和ID號由你自己選擇,現在我們來寫.rc文件的代碼。
1 #include "resource.h"
2
3 IDR_MYMENU MENU 4 BEGIN 5 POPUP "&File"
6 BEGIN 7 MENUITEM "E&xit", ID_FILE_EXIT 8 END 9
10 POPUP "&Stuff"
11 BEGIN 12 MENUITEM "&Go", ID_STUFF_GO 13 MENUITEM "G&o somewhere else", 0, GRAYED 14 END 15 END 16
17 IDI_MYICON ICON "menu_one.ico"
根據你使用的工具,將.rc文件加入你的項目或生成文件中。
你也要在你的源文件中使用#include “resource.h”引入資源頭文件。
在你的窗口中附加菜單和圖標最簡單的方式就是在注冊窗口類是指定好,就像下面:
1 wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU); 2
3 wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON)); 4 wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, 16, 16, 0);
在原來的代碼上面改看看會發生什么,你的窗口現在應該有一個File和Stuff的菜單,這種是你的資源文件被成功編譯並鏈接至程序中的情況,如果發生錯誤請檢查。
窗口右上角的圖標和任務欄上的指定的小圖標現在應該能夠顯示出來了,如果你使用Alt-Tab的組合鍵你會看到應用列表的大圖標。
我曾經用loadIcon()來加載小圖標因為它更簡單,但是這個方法加載的圖標的默認大小是32x32,為了加載小圖標我們需要使用LoadImage()方法。請注意圖標文件和資源可以包含多個圖像,在這種情況下我提供了加載資源圖標的兩個尺寸。
例子:菜單2
另一種使用菜單資源的選擇是動態創建(即你的程序運行的時候),這是一種更加聰明的工作方式,有時為了增加程序的靈活性是有必要的。
你也可以使用沒有存儲在資源中的圖標,而選擇在運行的時候單獨加載存儲圖標的文件,這也給了你一種選擇,即允許用戶在通用的對話框中選擇圖標,在后面我們會討論到,或者其他受動態創建影響的東西。
讓我們再一次從沒有資源文件和頭文件的simple_windows的代碼開始,現在我們將會處理WM_CREATE消息然后向我們的窗口中增加菜單。
1 #define ID_FILE_EXIT 9001
2 #define ID_STUFF_GO 9002
現在把這兩個id放在你的源文件的頭部,就在#include的下面,現在我們把下面的代碼加入到WM_CREATE消息的句柄中:
1 case WM_CREATE: 2 { 3 HMENU hMenu, hSubMenu; 4 HICON hIcon, hIconSm; 5
6 hMenu = CreateMenu(); 7
8 hSubMenu = CreatePopupMenu(); 9 AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit"); 10 AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File"); 11
12 hSubMenu = CreatePopupMenu(); 13 AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go"); 14 AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff"); 15
16 SetMenu(hwnd, hMenu); 17
18
19 hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE); 20 if(hIcon) 21 SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); 22 else
23 MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR); 24
25 hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE); 26 if(hIconSm) 27 SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm); 28 else
29 MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR); 30 } 31 break;
這個跟第一個例子的效果一樣,創建了一個菜單附加到我們的窗口中,當程序終止時,指定附加到窗口的菜單會自動被移除。所以我們不要擔心如何除掉它,如果我們想這樣做,可以使用GetMenu()和DestroyMenu()來達到目的。
加載圖標的代碼相當簡單,這里我們調用了兩次LoadImage(),分別加載16x16和32x32尺寸的圖標,我們不能使用LoadIcon()因為它只能加載資源文件,而不是圖標文件本身。我們為實例句柄參數指定了一個NULL的參數因為我們不是從一個模塊中加載資源的,我們通過傳遞我們想要加載的圖標文件名而不是資源ID。最后我們通過LR_LOADFROMFILE標識表明我們想要函數我們傳遞進入的字符串當做文件名而不是一個資源名。
如果圖標加載成功,我們會將圖標通過WM_SETICON分配給窗口;如果失敗了,會彈出一個告訴我們加載出錯的對話框。
注意:如果突變文件不在我們程序的當前工作目錄下那么調用LoadImage()會失敗。如果你使用VC++並且從IDE運行程序,當前工作目錄會是項目文件所在的目錄中的一個。如果你使用瀏覽器的調試或發布目錄或者命令行運行程序,那么你需要將圖標文件復制進相應的工作目錄中以便程序可以找到它;如果都不行,嘗試用絕對路徑"C:\\Path\\To\\Icon.ico"
OK,現在我們有自己的菜單了,我們需要讓它做些東西,這相當簡單,我們需要做的就是處理WM_COMMAND消息。同樣我們需要檢查得到了哪些命令然后根據它做處理,現在我們的WndProc()看起來影響像下面這樣了。
1 LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 2 { 3 switch(Message) 4 { 5 case WM_CREATE: 6 { 7 HMENU hMenu, hSubMenu; 8
9 hMenu = CreateMenu(); 10
11 hSubMenu = CreatePopupMenu(); 12 AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit"); 13 AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File"); 14
15 hSubMenu = CreatePopupMenu(); 16 AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go"); 17 AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff"); 18
19 SetMenu(hwnd, hMenu); 20
21
22 hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE); 23 if(hIcon) 24 SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); 25 else
26 MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR); 27
28 hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE); 29 if(hIconSm) 30 SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm); 31 else
32 MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR); 33 } 34 break; 35 case WM_COMMAND: 36 switch(LOWORD(wParam)) 37 { 38 case ID_FILE_EXIT: 39
40 break; 41 case ID_STUFF_GO: 42
43 break; 44 } 45 break; 46 case WM_CLOSE: 47 DestroyWindow(hwnd); 48 break; 49 case WM_DESTROY: 50 PostQuitMessage(0); 51 break; 52 default: 53 return DefWindowProc(hwnd, Message, wParam, lParam); 54 } 55 return 0; 56 }
正如你看到了我們得到了所有設置的WM_COMMAND,甚至它有自己的switch,這個switch的值是根據wParam的低字來判斷的,wParam對於WM_COMMAND來說包含了發送消息的控制或菜單id。
很明顯,我們想要退出菜單選項實現關閉程序的功能,所以在WM_COMMAND的ID_FILE_EXIT句柄下你可以使用以下的代碼來達到目的。
1 PostMessage(hwnd, WM_CLOSE, 0, 0);
你的WM_COMMAND消息處理程序看起來應該像下面這樣子:
1 case WM_COMMAND: 2 switch(LOWORD(wParam)) 3 { 4 case ID_FILE_EXIT: 5 PostMessage(hwnd, WM_CLOSE, 0, 0); 6 break; 7 case ID_STUFF_GO: 8
9 break; 10 } 11 break;
我留給你實現另一個ID_STUFF_GO菜單命令做點什么。
程序文件圖標
你可能注意到了menu_one.exe文件現在像我們用資源文件引入圖標的方式顯示了圖標,然而menu_two.exe並沒有這樣做因為我們引入了一個外部文件。窗口瀏覽器在第一個程序中通過資源文件非常簡單的顯示了一個圖標,因為只需要一個圖標;如果我們想通過程序顯示一個確切地圖標,那么就可以采取第一種方式,即簡單地使用資源ID;如果你想要根據你的選擇顯示圖標那么就采取第二種方式吧。
PS.由於本人英文水平所限,只能翻譯到這個程度了,有紕漏還望多多指出,附上本篇翻譯的英文原版教程地址:http://www.winprog.org/tutorial/menus.html