ITPub博客

首页 > IT职业 > IT生活 > iczelion tut22 (转)

iczelion tut22 (转)

原创 IT生活 作者:gugu99 时间:2007-10-31 17:06:49 0 删除 编辑
iczelion tut22 (转)[@more@]

Tutorial 22: Superclassing


In this tutorial, we will learn about superclassing, what it is and what it is for. You will also learn how to prov Tab key navigation to the controls in your own window.
the example

Theory:

In your programming career, you will surely encounter a situation where you need several controls with *slightly* different behavior. For example, you may need 10 edit controls which accept only number. There are several ways to achieve that goal:
  • Create your own class and instantiate the controls
  • Create those edit control and then subclass all of them
  • Superclass the edit control
The first method is too tedious. You have to implement every functionality of the edit control yourself. Hardly a task to be taken lightly. The second method is better than the first one but still too much work. It is ok if you subclass only a few controls but it's going to be a nightmare to subclass a dozen or so controls. Superclassing is the technique you should use for this occasion.
Subclassing is the method you use to *take control* of a particular window class. By *taking control*, I mean you can modify the properties of the window class to suit your purpose then then create the bunch of controls.
The steps in superclassing is outlined below:
  • call GetClassInfoEx to obtain the information about the window class you want to superclass. GetClassInfoEx requires a pointer to a WNDCLASSEX structure which will be filled with the information if the call returns succesully.
  • Modify the WNDCLASSEX members that you want. However, there are two members which you MUST modify:
    • hInstance  You must put the instance handle of your program into this member.
    • lpszClassName  You must provide it with a pointer to a new class name.
      You need not modify lpfnWndProc member but most of the time, you need to do it. Just remember to save the original lpfnWndProc member if you want to call it with CallWindowProc.
  • Register the modifed WNDCLASSEX structure. You'll have a new window class which has several characteristics of the old window class.
  • Create from the new class
Superclassing is better than subclassing if you want to create many controls with the same characteristics.

Example:

.386
.model flat,stdcall
option casemap:none
include masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
includelib masm32libuser32.lib
includelib masm32libkernel32.lib

WM_SUPERCLASS equ WM_USER+5
WinMain PROTO :D,:DWORD,:DWORD,:DWORD
EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD

.data
ClassName  "SuperclassWinClass",0
AppName  db "Superclassing Demo",0
EditClass  db "EDIT",0
OurClass db "SUPEREDITCLASS",0
Message  db "You pressed the Enter key in the text box!",0

.data?
hInstance dd ?
hwndEdit dd 6 dup(?)
OldWndProc dd ?

.code
start:
  invoke GetModuleHandle, NULL
  mov  hInstance,eax
  invoke WinMain, hInstance,NULL,NULL, 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_APPWORKSPACE
  mov wc.lpszMenuName,NULL
  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+WS_EX_CONTROLPARENT,ADDR ClassName,ADDR AppName,
  WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,
  CW_USEDEFAULT,350,220,NULL,NULL,
  hInst,NULL
  mov hwnd,eax

  .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 uses ebx edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  LOCAL wc:WNDCLASSEX
  .if uMsg==WM_CREATE
  mov wc.cbSize,sizeof WNDCLASSEX
  invoke GetClassInfoEx,NULL,addr EditClass,addr wc
  push wc.lpfnWndProc
  pop OldWndProc
  mov wc.lpfnWndProc, OFFSET EditWndProc
  push hInstance
  pop wc.hInstance
  mov wc.lpszClassName,OFFSET OurClass
  invoke RegisterClassEx, addr wc
  xor ebx,ebx
  mov edi,20
  .while ebx<6
  invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,
  WS_CHILD+WS_VISIBLE+WS_BORDER,20,
  edi,300,25,hWnd,ebx,
  hInstance,NULL
  mov dword ptr [hwndEdit+4*ebx],eax
  add edi,25
  inc ebx
  .endw
  invoke SetFocus,hwndEdit
  .elseif uMsg==WM_DESTROY
  invoke PostQuitMessage,NULL
  .else
  invoke DefWindowProc,hWnd,uMsg,wParam,lParam
  ret
  .endif
  xor eax,eax
  ret
WndProc endp

EditWndProc PROC hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
  .if uMsg==WM_CHAR
  mov eax,wParam
  .if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
  .if al>="a" && al<="f"
  sub al,20h
  .endif
  invoke CallWindowProc,OldWndProc,hEdit,uMsg,eax,lParam
  ret
  .endif
  .elseif uMsg==WM_KEYDOWN
  mov eax,wParam
  .if al==VK_RETURN
  invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
  invoke SetFocus,hEdit
  .elseif al==VK_TAB
  invoke GetKeyState,VK_SHIFT
  test eax,80000000
  .if ZERO?
  invoke GetWindow,hEdit,GW_HWNDNEXT
  .if eax==NULL
  invoke GetWindow,hEdit,GW_HWNDFIRST
  .endif
  .else
  invoke GetWindow,hEdit,GW_HWNDPREV
  .if eax==NULL
  invoke GetWindow,hEdit,GW_HWNDLAST
  .endif
  .endif
  invoke SetFocus,eax
  xor eax,eax
  ret
  .else
  invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
  ret
  .endif
  .else
  invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
  ret
  .endif
  xor eax,eax
  ret
EditWndProc endp
end start
 

Analysis:

The program will create a simple window with 6 "modified" edit controls in its client area. The edit controls will accept only hex digits. Actually, I modified the subclassing example to do superclassing. The program starts normally and the interesting part is when the main window is created:

  .if uMsg==WM_CREATE
  mov wc.cbSize,sizeof WNDCLASSEX
  invoke GetClassInfoEx,NULL,addr EditClass,addr wc

We must first fill the WNDCLASSEX structure with the data from the class which we want to superclass, in this case, it's EDIT class. Remember that you must set the cbSize member of the WNDCLASSEX structure before you call GetClassInfoEx else the WNDCLASSEX structure will not be filled proy. After GetClassInfoEx returns, wc is filled with all information we need to create a new window class.

  push wc.lpfnWndProc
  pop OldWndProc
  mov wc.lpfnWndProc, OFFSET EditWndProc
  push hInstance
  pop wc.hInstance
  mov wc.lpszClassName,OFFSET OurClass

Now we must modify some members of wc. The first one is the pointer to the window procedure. Since we need to chain our own window procedure with the original one, we have to save it into a variable so we can call it with CallWindowProc. This technique is identical to subclassing except that you modify the WNDCLASSEX structure directly without having to call SetWindowLong. The next two members must be changed else you will not be able to register your new window class, hInstance and lpsClassName. You must replace original hInstance value with hInstance of your own program. And you must choose a new name for the new class.

  invoke RegisterClassEx, addr wc

When all is ready, register the new class. You will get a new class with some characteristics of the old class.

  xor ebx,ebx
  mov edi,20
  .while ebx<6
  invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,
  WS_CHILD+WS_VISIBLE+WS_BORDER,20,
  edi,300,25,hWnd,ebx,
  hInstance,NULL
  mov dword ptr [hwndEdit+4*ebx],eax
  add edi,25
  inc ebx
  .endw
  invoke SetFocus,hwndEdit

Now that we registered the class, we can create windows based on it. In the above snippet, I use ebx as the counter of the number of windows created. edi is used as the y coordinate of the left upper corner of the window. When a window is created, its handle is stored in the array of dwords. When all windows are created, set input focus to the first window.
At this point, you got 6 edit controls which accept only hex digits. The substituted window proc handles the filter. Actually, it's identical to the window proc in subclassing example. As you can see, you don't have to do extra work of subclassing them.

I throw in a code snippet to handle control navigation with tabs to make this example more juicy. Normally, if you put controls on a dialog box, the dialog box manager handles the navigation keys for you so you can tab to go to the next control or shift-tab to go back to the previous control. Alas, such feature is not available if you put your controls on a simple window. You have to subclass them so you can handle the Tab keys yourself. In our example, we need not subclass the controls one by one because we already superclassed them, so we can provide a "central control navigation manager" for them.
 

  .elseif al==VK_TAB
  invoke GetKeyState,VK_SHIFT
  test eax,80000000
  .if ZERO?
  invoke GetWindow,hEdit,GW_HWNDNEXT
  .if eax==NULL
  invoke GetWindow,hEdit,GW_HWNDFIRST
  .endif
  .else
  invoke GetWindow,hEdit,GW_HWNDPREV
  .if eax==NULL
  invoke GetWindow,hEdit,GW_HWNDLAST
  .endif
  .endif
  invoke SetFocus,eax
  xor eax,eax
  ret

The above code snippet is from EditWndClass procedure. It checks if the user press Tab key, if so, it call GetKeyState to check if  the SHIFT key is also pressed. GetKeyState returns a value in eax that detenes whether the specified key is pressed or not. If the key is pressed, the high bit of eax is set. If not, the high bit is clear. So we test the return value against 80000000h. If the high bit is set, it means the user pressed shift+tab which we must handle separately.
If the user press Tab key alone, we call GetWindow to retrieve the handle of the next control. We use GW_HWNDNEXT flag to tell GetWindow to obtain the handle to the window that is next in line to the current hEdit. If this function returns NULL, we interpret it as no more handle to obtain so the current hEdit is the last control in the line. We will "wrap around" to the first control by calling GetWindow with GW_HWNDFIRST flag. Similar to the Tab case, shift-tab just works in reverse.


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10748419/viewspace-980180/,如需转载,请注明出处,否则将追究法律责任。

上一篇: iczelion tut21 (转)
下一篇: iczelion tut23 (转)
请登录后发表评论 登录
全部评论
  • 博文量
    3121
  • 访问量
    2354071