選單(轉)

heying1229發表於2007-07-28
選單:

  本課中我們將在我們的應用程式中加入一個選單。
理論:
選單可以說是WINDOWS最重要的元素之一。有了它,使用者可以方便地選擇操作命令.使用者只要細讀一下所有的選單項就可以明瞭應用程式所提供的大概功能,而且可以立即操作,無須去閱讀手冊了.正因為選單給了使用者一種方便的方式,所以您在應用程式中加入選單時就要遵守一般的標準.譬如:一般頭兩個選單項是"File"和"Edit",最後是"Help",您可以在這中間插入您要定義的選單項.如果所執行的選單命令會彈出一個對話方塊,那麼就要在該選單項後加入省略符(...).選單是一種資源,除選單外還有其它像對話方塊,字串,圖示,點陣圖資源等.在連結時連結程式將把資源加入到可執行程式中去,最後我們的執行程式中就既包括機器指令又包括了資源. 您可以在任何文字編輯器中編寫指令碼檔案,在檔案中您可以指定資源呈現出來的外觀和其它的一些屬性.當然更直觀的方法是用資源編輯器,通常資源編輯器都打包在編譯環境中,像Visual C++, Borland C++等都帶了資源編輯器. 我們可以按以下方式來定義一個選單資源:

MyMenu MENU
{
[menu list here]
}
這和C語言中的結構體的定義非常相似。 MyMenu類似於被定義的變數,而MENU則類似於關鍵字。當然您可以用另外一種辦法,那就是用BEGIN和END來代替花括號,這和PASCAL語言中的風格相同。
在選單項的列表中是一大串的MENUITEM和POPUP語句。MENUITEM定義了一個選單項,當選擇後不會啟用對話方塊。它的語法如下:
MENUITEM "&text", ID [,options]
它由關鍵字MENUITEM開頭,緊跟在MENUITEM後的是指選單項的名稱字串,符號“&“後的第一個字元將會帶下畫線,它也是該選單項的快捷鍵。ID的作用當該選單被選中時,WINDOWS的訊息處理過程用來區分選單項用的。毫無疑問,ID號必須唯一。options有以下可供選擇的屬性:
GRAYED 代表該選單項處於非啟用狀態,即當其被選中時不會產生WM_COMMAND訊息。該選單以灰色顯示。
INACTIVE 代表該選單項處於非啟用狀態,即當其被選中時不會產生WM_COMMAND訊息。該選單以正常顏色顯示。
MENUBREAK 該選單項和隨後的選單項會顯示在新列中。(譯者注:比較難描述,請做實驗。)
HELP 該選單項和隨後的選單項右對齊。(譯者注:我在WINDOWS2000下編譯有該標誌的選單項,該標誌好像沒起作用)
您可以單獨使用以上標誌位,也可以把它們或在一起。當然INACTIVE和GRAYED不能同時使用。 POPUP的語法如下:
POPUP "&text" [,options]
{
[menu list]
}
POPUP定義了一個選單項當該選單項被選中時又會彈出一個子選單。另外有一種特別型別的MENUITEM語句MENUITEM SEPARATOR,它表示在選單項位置畫一條分隔線。定義完選單後,您就可以在程式中使用指令碼中定義的選單資源了。您可以在程式的兩個地方(或叫做用兩種方式)使用它們:
在WNDCLASSEX結構體的成員lpszMenuName中。譬如,您有一個選單“FirstMenu“,您可以按如下方法把它聯絡到您的視窗:
.DATA
MenuName db "FirstMenu",0
...........................
...........................
.CODE
...........................
mov wc.lpszMenuName, OFFSET MenuName
...........................
在CreateWindowEx函式中指明選單的控制程式碼:
.DATA
MenuName db "FirstMenu",0
hMenu HMENU ?
...........................
...........................
.CODE
...........................
invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu, eax
invoke CreateWindowEx,NULL,OFFSET ClsName,
OFFSET Caption, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL,
hMenu,
hInst,
NULL
...........................
您也許會問,這兩著之間有什麼不同呢?當您用第一種方法時,由於是在視窗類中指定,故所有由該視窗類派生的視窗都將有相同的選單。如果您想要從相同的類中派生的視窗有不同的選單那就要使用第二中方法,該方法中透過函式CreateWindowEx指定的選單會“覆蓋”WNDCLASSEX結構體中指定的選單。接下來我們看看當使用者選擇了一個選單項時它是如何通知WINDOWS 視窗過程的:當使用者選擇了一個選單項時,WINDOWS視窗過程會接收到一個WM_COMMAND訊息,傳進來的引數wParam的底位元組包含了選單項的ID號。好了,上面就是關於選單項的一切,下面我們就來實踐。
例子:
第一個例子顯示了指定一個選單項的第一種方法:
.386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
includelib masm32libuser32.lib
includelib masm32libkernel32.lib

.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MenuName db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.const
IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4

.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName ; Put our menu name here
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
**************************************************************************************************************************

Menu.rc
**************************************************************************************************************************
#define IDM_TEST 1
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4

FirstMenu MENU
{
POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
MENUITEM "&Test", IDM_TEST
}

分析:
我們先來分析資原始檔:

#define IDM_TEST 1 /* equal to IDM_TEST equ 1*/
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4

上面的幾行定義了選單項的ID號。只要注意選單項ID號必須唯一外,您可以給ID號任何值。
FirstMenu MENU

用關鍵字MENU定義選單。

POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}

定義一個有四個選單項的子選單,其中第三個選單項是一個分隔線。

MENUITEM "&Test", IDM_TEST

定義主選單中的一項。下面我們來看看原始碼。

MenuName db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0

MenuName是資原始檔中指定的選單的名字。因為您可以在指令碼檔案中定義任意多個選單,所以在使用前必須指定您要使用那一個,接下來的行是在選中選單項時顯示在相關對話方塊中的字串。

IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4

定義用在WINDOWS視窗過程中的選單項ID號。這些值必須和指令碼檔案中的相同。
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF

在本視窗過程中我們處理WM_COMMAND訊息。當使用者選擇了一個選單項時,該選單項的ID放入引數wParam中被同時送到WINDOWS的視窗過程,我們把它儲存到eax暫存器中以便和預定義的選單項ID比較用。前三種情況下,當我們選中Test、Say Hello、Say GoodBye選單項時,會彈出一個對話方塊其中顯示一個相關的字串,選擇Exit選單項時,我們就呼叫函式DestroyWindow,其中的引數是我們視窗的控制程式碼,這樣就銷燬了視窗。就像您所看到的,透過在一個視窗類中指定選單名的方法來給一個應用程式生成一個選單是簡單而直觀的。除此方法外您還可以用另一種方法,其中資原始檔是一樣的,原始檔中也只有少數的改動,這些改動如下:

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HMENU ? ; handle of our menu

定義了一個變數來儲存我們的選單的控制程式碼,然後:
invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu,eax
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,
hInst,NULL

呼叫LoadMenu函式,該函式需要例項控制程式碼和選單名的字串,呼叫的結果返回指向選單的控制程式碼,然後傳給函式CreateWindowEx剛返回的選單控制程式碼就可以了。

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10172717/viewspace-928721/,如需轉載,請註明出處,否則將追究法律責任。

相關文章