全部博文(290)
分类:
2007-12-16 23:26:45
Tutorial 9: Child Window Controls
第九课:子窗口控件
In this tutorial, we will explore child window controls which are very important input and output devices of our programs.
在这一课, 我们将探究子窗口控件,这些子窗口控件在我们的应用程序中是非常重要的输入输出设备。
Download the example here.
Theory:
原理:
Windows provides several predefined window classes which we can readily use in our own programs. Most of the time we use them as components of a dialog box so they're usually called child window controls. The child window controls process their own mouse and keyboard messages and notify the parent window when their states have changed. They relieve the burden from programmers enormously so you should use them as much as possible. In this tutorial, I put them on a normal window just to demonstrate how you can create and use them but in reality you should put them in a dialog box.
Widnows提供了几个预定义的窗口类,我们能在自己的程序中欣然的使用它们。
大多数时候我们把它们作为对话框的一部分。(元件)所以通常它们也称呼为子窗口控件。 这些子窗口控件处理它们自己的鼠标和键盘消息或者是当它们的状态被改变时通知父窗口。它们减轻了程序员巨大的负担所以你应该尽可能多的利用它们。 在这一课中,我将它们放在一标准的窗口上只不过是作为示范来告诉大家如何创建和使用它们但是在实际中你应该把它们放在对话框中。
Examples of predefined window classes are button, listbox, checkbox, radio button,edit etc.
例子中使用的子窗口控件类是:按钮类,列表框,复选框,单选按钮,编辑框等等。
In order to use a child window control, you must create it with CreateWindow or CreateWindowEx. Note that you don't have to register the window class since it's registered for you by Windows. The class name parameter MUST be the predefined class name. Say, if you want to create a button, you must specify "button" as the class name in CreateWindowEx. The other parameters you must fill in are the parent window handle and the control ID. The control ID must be unique among the controls. The control ID is the ID of that control. You use it to differentiate between the controls.
为了使用子窗口控件,你必须在CreateWindow或者是CreateWindowEx函数中使用它。注意:你没有必要去注册这些窗口类,因为windows已经注册过了。作为参数的类名必须是预定义的类名。比方说,如果你想创建一个按钮,你必须在createWindowEx函数中指定“button”作为窗口类参数名。其他必须被填写的参数还有父窗口的句柄和控件ID值。控件ID值必须是唯一的。控件的ID值就是你使用的那个控件的标识,你用它来区分这些控件。
After the control was created, it will send messages notifying the parent window when its state has changed. Normally, you create the child windows during WM_CREATE message of the parent window. The child window sends WM_COMMAND messages to the parent window with its control ID in the low word of wParam, the notification code in the high word of wParam, and its window handle in lParam. Each child window control has different notification codes, refer to your Win32 API reference for more information.
The parent window can send commands to the child windows too, by calling SendMessage function. SendMessage function sends the specified message with accompanying values in wParam and lParam to the window specified by the window handle. It's an extremely useful function since it can send messages to any window provided you know its window handle.
在这些控件被创建后,当其状态改变时,会向其父窗口发送消息。通常,你在父窗口的WM_CREATE消息中创建这些子控件窗口。 子控件发送WM_COMMAND消息给父窗口 , wParam参数的低字节是这个子窗口控件的ID值,wParam参数高字节是通知码,它的窗口句柄则放在lParam参数中。每一个子窗口控件都有不同的通知码,查阅你的Win32API手册可以获得更多的信息。父窗口也能发送命令给子窗口控件,但需要调用SendMessage函数。SendMessage函数发送一个指定的消息给父窗口,消息的附加值在wParam中并在lParam参数中指定子控件窗口句柄。这是一个非常有用的函数,因为你可以用它发送任何消息给任何窗口。只要你知道接收消息的窗口句柄。
So, after creating the child windows, the parent window must process WM_COMMAND messages to be able to receive notification codes from the child windows.
所以,在创建了子窗口后,父窗口必须处理WM_COMMAND消息才能从子窗口中接收到通知码。
Example:
We will create a window which contains an edit control and a pushbutton. When you click the button, a message box will appear showing the text you typed in the edit box. There is also a menu with 4 menu items:
我们将创建一个窗口,这个窗口包含一个编辑框和一个按钮。当你点击这个按钮时,一个消息框将出现并显示你在编辑框中输入的文本。这个窗口还有一个菜单,其中包含四个菜单项:
放置一个字符串在编辑框中
清除编辑框中的内容
弹出一个消息框并显示编辑框中的文本
退出程序。
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MenuName db "FirstMenu",0
ButtonClassName db "button",0
ButtonText db "My First Button",0
EditClassName db "edit",0
TestString db "Wow! I'm in an edit box now",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndButton HWND ?
hwndEdit HWND ?
buffer db 512 dup(?) ; buffer to store the text retrieved from the edit box
.const
ButtonID equ 1 ; The control ID of the button control
EditID equ 2 ; The control ID of the edit control
IDM_HELLO equ 1
IDM_CLEAR equ 2
IDM_GETTEXT 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_BTNFACE+1
mov wc.lpszMenuName,OFFSET MenuName
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,WS_EX_CLIENTEDGE,ADDR ClassName, \
ADDR AppName, WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT, CW_USEDEFAULT,\
300,200,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 TranslateMessage, ADDR msg
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_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or\
ES_AUTOHSCROLL,\
50,35,200,25,hWnd,8,hInstance,NULL
mov hwndEdit,eax
invoke SetFocus, hwndEdit
invoke CreateWindowEx,NULL, ADDR ButtonClassName,ADDR ButtonText,\
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,\
75,70,140,25,hWnd,ButtonID,hInstance,NULL
mov hwndButton,eax
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
.IF ax==IDM_HELLO
invoke SetWindowText,hwndEdit,ADDR TestString
.ELSEIF ax==IDM_CLEAR
invoke SetWindowText,hwndEdit,NULL
.ELSEIF ax==IDM_GETTEXT
invoke GetWindowText,hwndEdit,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
.IF ax==ButtonID
shr eax,16
.IF ax==BN_CLICKED
invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
.ENDIF
.ENDIF
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analysis:
Let's analyze the program.
.ELSEIF uMsg==WM_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE, \
ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT\
or ES_AUTOHSCROLL,\
50,35,200,25,hWnd,EditID,hInstance,NULL
mov hwndEdit,eax
invoke SetFocus, hwndEdit
invoke CreateWindowEx,NULL, ADDR ButtonClassName,\
ADDR ButtonText,\
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,\
75,70,140,25,hWnd,ButtonID,hInstance,NULL
mov hwndButton,eax
We create the controls during processing of WM_CREATE message. We call CreateWindowEx with an extra window style, WS_EX_CLIENTEDGE, which makes the client area look sunken. The name of each control is a predefined one, "edit" for edit control, "button" for button control. Next we specify the child window's styles. Each control has extra styles in addition to the normal window styles. For example, the button styles are prefixed with "BS_" for "button style", edit styles are prefixed with "ES_" for "edit style". You have to look these styles up in a Win32 API reference. Note that you put a control ID in place of the menu handle. This doesn't cause any harm since a child window control cannot have a menu.
After creating each control, we keep its handle in a variable for future use.
我们在处理WM_CREATE消息的时候创建这些控件。我们调用CreateWindowEx函数并使用了一个扩展窗口样式,WS_EX_CLIENTEDGE,它使得客户区看起来是凹陷的。每一个控件的名字都是预定义的,“edit”是编辑框控件, “button”是按钮控件。随后我们将指定这个子窗口的样式。除了窗口样式外每一个子窗口控件都有一个扩展样式。例如:按钮类用“BS_"作为按钮样式的前缀。编辑框用
“ES_”作为编辑框样式的前缀。你可以在win32api手册中查看这些样式。注意,你应该把控件ID放在菜单句柄的位置。这并不会引起任何损伤因为一个子窗口控件不能拥有任何菜单。
在创建了一个控件后,我们将它的句柄存储在一变量中以便我们将来使用。
SetFocus is called to give input focus to the edit box so the user can type the text into it immediately.
Now comes the really exciting part. Every child window control sends notification to its parent window with WM_COMMAND.
SetFocus函数被调用后给编辑框一个输入焦点所以用户可以立即在它里面键入文本。
现在来到让我们真正兴奋的部分。每一个子窗口控件发送WM_COMMAND消息来通知它的父窗口。
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
Recall that a menu also sends WM_COMMAND messages to notify the window about its state too. How can you differentiate between WM_COMMAND messages originated from a menu or a control? Below is the answer
回想一下,我们说过,当一个菜单的状态改变时,它也会发送一个WM_CMMAND消息来通知窗口。 那么我们如何区分一个WM_COMMAND消息是来源于一个菜单还是一个控件呢?答案请看下面:
|
Low word of wParam |
High word of wParam |
lParam |
Menu |
Menu ID |
0 |
0 |
Control |
Control ID |
Notification code |
Child Window Handle |
You can see that you should check lParam. If it's zero, the current WM_COMMAND message is from a menu. You cannot use wParam to differentiate between a menu and a control since the menu ID and control ID may be identical and the notification code may be zero.
你能从这个表看出,你应该检测lparam参数来区分消息(wmcommand)的来源。如果它的值是0,当前的WM_COMMAND消息来自于菜单。你不能用wparam参数来区分这个消息是来源于菜单还是控件。因为菜单的ID值和控件ID值可能相同并且通知码也可能都是0。
.IF ax==IDM_HELLO
invoke SetWindowText,hwndEdit,ADDR TestString
.ELSEIF ax==IDM_CLEAR
invoke SetWindowText,hwndEdit,NULL
.ELSEIF ax==IDM_GETTEXT
invoke GetWindowText,hwndEdit,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
You can put a text string into an edit box by calling SetWindowText. You clear the content of an edit box by calling SetWindowText with NULL. SetWindowText is a general purpose API function. You can use SetWindowText to change the caption of a window or the text on a button.
To get the text in an edit box, you use GetWindowText.
你能调用SetWindowText函数来放置一文本在一个编辑框中。 你也可以在SetWindowText函数中传入NULL值来清除编辑框的内容。SetWindowText是一个通用的API函数。你能用SetWindowText函数来改变一个窗口的标题或者是一个按钮的文本。为了获得在编辑框中的文本,你可以用GetWindowText函数。
.IF ax==ButtonID
shr eax,16
.IF ax==BN_CLICKED
invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
.ENDIF
.ENDIF
The above code snippet deals with the condition when the user presses the button. First, it checks the low word of wParam to see if the control ID matches that of the button. If it is, it checks the high word of wParam to see if it is the notification code BN_CLICKED which is sent when the button is clicked.
上面这个代码片断执行的条件是当用户按这个按钮时。首先,它检查wParam的低字节来看这个控件的ID是否与那个按钮相匹配。如果是,它检查wParam的高字节来看它是不是当按钮被单击时发送的通知码BN_CLICKED。
The interesting part is after it's certain that the notification code is BN_CLICKED. We want to get the text from the edit box and display it in a message box. We can duplicate the code in the IDM_GETTEXT section above but it doesn't make sense. If we can somehow send a WM_COMMAND message with the low word of wParam containing the value IDM_GETTEXT to our own window procedure, we can avoid code duplication and simplify our program. SendMessage function is the answer. This function sends any message to any window with any wParam and lParam we want. So instead of duplicating the code, we call SendMessage with the parent window handle, WM_COMMAND, IDM_GETTEXT, and 0. This has identical effect to selecting "Get Text" menu item from the menu. The window procedure doesn't perceive any difference between the two.
有兴趣的部分是在它确认了那个通知码是BN_CLICKED后。我们想从编辑框获取文本并将它显示在消息框中。我们能复制上面IDM_GETTEXT节区中的代码但是它并没有什么意义。如果我们能在任何时候发送一条wparam参数低字节包含IDM_GETTEXT值的WM_COMMAND消息给我们自己窗口过程。我们就能消除代码复制并使我们的程序简化。SendMessage函数就是答案。这个函数能发送任何包含你想要的wparam 参数值 和lParam参数值的任何消息给任何窗口。所以代替复制代码的方法就是,我们传递父窗口句柄,WM_COMMAND消息,IDM_GETTEXT, 和0给 SendMessage函数并调用它。这与从菜单中选择"Get Text"菜单项有着同样的效果。窗口过程感觉不出它们之间的任何变化。 (指用SendMessage函数发送消息和在菜单中选择菜单)
You should use this technique as much as possible to make your code more organized.
你应该尽可能用这种技巧来组织你的代码。
Last but not least, do not forget the TranslateMessage function in the message loop. Since you must type in some text into the edit box, your program must translate raw keyboard input into readable text. If you omit this function, you will not be able to type anything into your edit box.
最后,不要忘记在消息循环中用TranslateMessage函数。因为你必须输入一些文本在编辑框中,所以你的程序必须将未处理的键入翻译成可读的文本。如果你忽略了这个函数,你将不能在你的编辑框中输入任何东西。
This article come from Iczelion's asm page
风向改变 翻译 于 2007年12月16日