第十八课 通用控件
| 
       | 
    
       | 
| ToolbarWindow32 | Toolbar | 
| tooltips_class32 | Tooltip | 
| msctls_statusbar32 | Status bar | 
| SysTreeView32 | Tree view | 
| SysListView32 | List view | 
| SysAnimate32 | Animation | 
| SysHeader32 | Header | 
| msctls_hotkey32 | Hot-key | 
| msctls_progress32 | Progress bar | 
| RICHEDIT | Rich edit | 
| msctls_updown32 | Up-down | 
| SysTabControl32 | Tab | 
Property sheets、property pages和image list控件有它们自己的创建函数。Drag list其实是可以伸缩的listbox控件,所以它没有自己的类名。上面的类名是VC++的资源编辑器提供的,它们和Borland公司的WIN32 API指南中提出的不一样,和Petzold的书《Programming Windows 95》也不一样。可以肯定的是我们上面列出的类名绝对准确。 这些通用控件可以有通用的窗口类的一些风格,譬如WS_CHILD等。它们当然还有其他的特殊风格,譬如树型视图控件就有TVS_XXXXX风格,列表控件就有LVS_xxxx风格。具体的最好查找有关的WIN32 API函数指南。 既然我们已经知道了如何创建一个通用控件,我们就可以讨论这些通用控件之间以及和它们的父窗口之间是如何通讯的了。不象子窗口控件,通用控件在某些状态发生变化时不是通过发送WM_COMMAND而是发送WM_NOTIFY消息和父窗口通讯的。父窗口可以通过发送消息来控制子窗口的行为。对于那些新的通用控件,还有一些新的消息类型。您可以参考您的WIN32 API手册。
在下面的例子中我们将要实验一下进度条和状态条。
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
.const 
IDC_PROGRESS equ 
1            ; control 
IDs 
IDC_STATUS equ 2 
IDC_TIMER  equ 3 
.data 
ClassName  db "CommonControlWinClass",0 
AppName    db "Common Control Demo",0 
ProgressClass  db 
"msctls_progress32",0       ; the class name of 
the progress bar 
Message  db "Finished!",0 
TimerID  dd 0 
.data? 
hInstance  HINSTANCE ? 
hwndProgress dd 
? 
hwndStatus dd ? 
CurrentStep dd ? 
.code 
start: 
    invoke GetModuleHandle, NULL 
    mov    hInstance,eax 
    invoke WinMain, hInstance,NULL,NULL, 
SW_SHOWDEFAULT 
    invoke ExitProcess,eax 
    invoke InitCommonControls 
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,ADDR 
ClassName,ADDR AppName,\ 
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\ 
           
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,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 hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
    .if uMsg==WM_CREATE 
         invoke 
CreateWindowEx,NULL,ADDR ProgressClass,NULL,\ 
            
WS_CHILD+WS_VISIBLE,100,\ 
            
200,300,20,hWnd,IDC_PROGRESS,\ 
            
hInstance,NULL 
        mov 
hwndProgress,eax 
        mov 
eax,1000               
; the lParam of PBM_SETRANGE message contains the range 
        mov CurrentStep,eax 
        shl 
eax,16                   
; the high range is in the high word 
        invoke 
SendMessage,hwndProgress,PBM_SETRANGE,0,eax 
        invoke 
SendMessage,hwndProgress,PBM_SETSTEP,10,0 
        invoke 
CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS 
        mov hwndStatus,eax 
        invoke 
SetTimer,hWnd,IDC_TIMER,100,NULL        ; 
create a timer 
        mov 
TimerID,eax 
    .elseif uMsg==WM_DESTROY 
        invoke 
PostQuitMessage,NULL 
        .if 
TimerID!=0 
            invoke 
KillTimer,hWnd,TimerID 
        
.endif 
    .elseif 
uMsg==WM_TIMER        ; when a timer event 
occurs 
        invoke 
SendMessage,hwndProgress,PBM_STEPIT,0,0    ; step up the progress 
in the progress bar 
        sub 
CurrentStep,10 
        .if 
CurrentStep==0 
            invoke 
KillTimer,hWnd,TimerID 
            mov 
TimerID,0 
            invoke 
SendMessage,hwndStatus,SB_SETTEXT,0,addr Message 
            invoke 
MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION 
            invoke 
SendMessage,hwndStatus,SB_SETTEXT,0,0 
            invoke 
SendMessage,hwndProgress,PBM_SETPOS,0,0 
        .endif 
    .else 
        invoke 
DefWindowProc,hWnd,uMsg,wParam,lParam 
        ret 
    .endif 
    xor eax,eax 
    ret 
WndProc endp 
end start 
如果SetTimer调用成功的话就会返回计时器的ID号值,否则返回0。这也是为什么计时器的ID号必须为非零值的原因。