WIN32彙編: 18.通用控件

第十八課 通用控件


本課中我們將學習什麼是通用控件和如何使用它們。

理論:

WIN95相對於WIN3X有幾個加強的用戶界面控件。其實在WIN95正式發行前這些控件就在使用,譬如:狀態條、工具條等。以前程序員要自己去編程使用它們,現在微軟已經把它們包含到了WIN9X和WINNT中了。
  • Toolbar ---工具條
  • Tooltip ---提示文本
  • Status bar ---狀態條
  • Property sheet ---屬性頁單
  • Property page ---屬性頁
  • Tree view ---樹型視圖
  • List view ---列表視圖
  • Animation ---動畫
  • Drag list ---能夠處理Drag-Drop的列表框
  • Header ---
  • Hot-key ---熱鍵
  • Image list ---圖象鏈表
  • Progress bar ---進程狀態條
  • Right edit ---
  • Tab ---跳格表
  • Trackbar ---跟蹤條
  • Up-down ---滾動條
因爲通用控件的數量非常多,把它們全部裝入內存並註冊它們是非常浪費內存的。除了“RTF文本編輯”控件外其他控件的可執行代碼都放在comctl32.dll中,這樣其他的應用程序就可以使用它們了。“RTF文本編輯”控件在richedXX.dll中,由於該控件非常的複雜,所以也比其它控件大。
要加載comctl32.dll可以在您的應用程序中調用函數InitCommonControls。InitCommonControls函數是動態鏈接庫comctl32.dll中的一個函數,只要在您的程序中的任意地方引用了該函數就、會使得WINDOWS的程序加載器PE Loader加載該庫。函數InitCommonControls其實只有一條指令“ret”,它的唯一目的是爲了使得在調用了個該函數的應用程序的可執行文件的PE頭中的“引入”段中包含有comctl32.dll,這樣無論什麼時候該應用程序都會爲您加載該庫。所以真正初始化的工作是在該庫的入口點處做的,在這裏會註冊所有的通用控件類,然後所有的通用控件就可以在這些類上進行創建,這就象創建其它的子窗口控件一樣。
RTF文本編輯控件則不同。如果您要使用它,就必須調用LoadLibrary函數來動態加載,並調用FreeLibrary來動態地卸載。
現在我們學習如何創建這些通用控件。您可以用資源編輯器把它們放到一個對話框中,或者您也可以自己調用相關的函數來手動創建它們。幾乎所有的通用控件都是調用函數CreateWindowEx或CreateWindow來創建的,您只要在其中傳遞通用控件的類名即可。有一些通用控件有一些特別的創建函數,但是其實這些函數在內部都調用了CreateWindowEx,只是包裝後的函數更方便使用而已。經過包裝的函數有:
  • CreateToolbarEx
  • CreateStatusWindow
  • CreatePropertySheetPage
  • PropertySheet
  • ImageList_Create
爲了創建通用控件您必須要知道它們的類名,我們把類名列於如下:
 
類名
通用控件
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手冊。

在下面的例子中我們將要實驗一下進度條和狀態條。

例子代碼:

.386
.model flat,stdcall
option casemap:none
include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
include /masm32/include/comctl32.inc
includelib /masm32/lib/comctl32.lib
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

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

分析:

    invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
    invoke ExitProcess,eax
    invoke InitCommonControls 我故意把函數InitCommonControls放到ExitProcess後,這樣就可以驗證調用該函數僅僅是爲了在我們程序的可執行文件的PE頭中的引入段中放入引用了comctl32.dll的信息。您可以看到,即使該函數什麼都沒有做,我們的通用控件對話框依舊可以正常工作。     .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 在這裏我們創建了通用控件。注意CreateWindowEx函數中的參數hWnd是父窗口的句柄。另外它也指定了通用控件的ID號。因爲我們直接使用控件的窗口句柄,所以就沒有使用該ID號。所有的窗口都必須具有WS_CHILD風格。         mov eax,1000
        mov CurrentStep,eax
        shl eax,16
        invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax
        invoke SendMessage,hwndProgress,PBM_SETSTEP,10,0 在創建了進度條後我們先設定它的範圍。缺省的範圍是0-100。如果您不滿意,可以重新設置,這通過傳遞PBM_SETRANGE消息來實現。參數lParam中包含了範圍值,其中底字和高字分別是範圍的起始和終了的值。您可以指定進度條每移動一格的步長。本例子中把步長設置成10,意味着每發送一次PBM_STEPIT消息給進度條,它的顯示指針就會移動10。當然您可以調用PBM_SETPOS 來直接設定進度條上的指針的位置。用該消息您可以更方便地設定進度條了。         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 下面我們調用CreateStatusWindow來創建狀態條。這個調用很好理解,無需我多解釋。在狀態條創建後我們創建一個計時器。在本例中我們每隔100毫秒就更新一次進度條。下面時創建記時器的函數原型: SetTimer PROTO hWnd:DWORD, TimerID:DWORD, TimeInterval:DWORD, lpTimerProc:DWORD hWnd : 父窗口的句柄。
TimerID : 計時器的ID號。您可以指定一個唯一的非零值。
TimerInterval : 以毫秒計的時間間隔。
lpTimerProc : 計時器回調函數的地址。每當時間間隔到了的時候,該函數就會被系統調用。如果該值爲NULL,計時器就會把WM_TIMER消息發送到父窗口。

如果SetTimer調用成功的話就會返回計時器的ID號值,否則返回0。這也是爲什麼計時器的ID號必須爲非零值的原因。

    .elseif uMsg==WM_TIMER
        invoke SendMessage,hwndProgress,PBM_STEPIT,0,0
        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 當指定的時間到了的時候,計時器將發送WM_TIMER消息。您可以在處理該消息時作適當的處理。本例中我們將更新進度條,並檢查進度條是否超過最大的值。如果超過了的話,我們通過發送SB_SETTEXT消息來在狀態條中設置文本。這時,彈出一個對話框,當用戶關閉掉對話框後,我們去除掉進度條和狀態條中的文本。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章