Iczelion - 18 - Standard Steuerelemente

Tutorial 18: Standard Steuerelemente


Wir werden lernen was Standard-Steuerelemente sind und wie sie benutzt werden. Dieses Tutorial wird nur eine kurze Einführung sein.
Laden Sie den Beispiel Source Code hier herunter.

Theorie:

Windows 95 kommt mit verschiedenen Benutzer-Schnittstellen Erweiterungen gegenüber Windows 3.1x daher. Sie machen die GUI reicher. Verschiedene von ihnen wurden schon viel genutzt, bevor Windows 95 mit ihnen aufkam, so wie Status Bar, Toolbars, etc. Programmierer mussten sie nur selbst programmieren. Nun hat Microsoft sie in Windows 9x und NT integriert. Wir werden hier etwas über sie lernen.
Das sind die neuen Steuerelemente:

  • Toolbar
  • Tooltip
  • Status bar
  • Property sheet
  • Property page
  • Tree view
  • List view
  • Animation
  • Drag list
  • Header
  • Hot-key
  • Image list
  • Progress bar
  • Right edit
  • Tab
  • Trackbar
  • Up-down
Da es viele von ihnen gibt, wäre ein Laden aller in den Speicher und registrieren eine Verschwendung an Ressourcen. Alle von ihnen, mit Ausnahme des Rich Edit Steuerelements, sind in der comctl32.dll gespeichert, welche von Applikationen geladen werden kann, wenn diese die Steuerelemente verwenden möchte. Das Rich Edit Steuerelement befindet sich in einer eigenen DLL, richedXX.dll, da es wesentlich komplizierter und um einiges größer als die anderen ist.
Sie können comctl32.dll laden indem Sie InitCommonControls in Ihrem Programm aufrufen. InitCommonControls ist eine Funktion in der comctl32.dll, so dass wenn Sie sich darauf irgendwo in Ihrem Code beziehen, wird der PE Loader comctl32.dll laden, wenn Ihr Programm läuft. Sie müssen sie nicht ausführen, nur irgendwo im Code haben. Diese Funktion macht NICHTS! Ihr einziger Befehl ist ein "ret". Ihr einziger Daseins-Grund ist, die comctl32.dll in der Import Sektion zu referenzieren, so dass der PE Loader sie lädt, wann immer das Programm geladen wird. Das eigentlich Arbeitspferd ist die DLL-Einsprungspunkt-Funktion, welche alle Standard-Steuerelemente registriert, wenn die DLL geladen wird. Standard-Steuerelemente werden auf Klassen-basierend erzeugt, so wie andere Child-Fenster-Steuerelemente, so wie Edit, Listbox, etc.
Rich Edit ist wieder ein ganz anderes Thema. Wenn Sie es benutzen wollen, müssen Sie LoadLibrary aufrufen, um sie explizit zu laden und FreeLibrary um sie wieder zu entfernen.
Nun lernen wir, wie man sie erzeugt. Sie können einen Ressource-Editor benutzen, um sie in Dialogen zu plazieren oder Sie erzeugen sie selbst. Fast alle Standard-Steuerlemente werden durch einen Aufruf von CreateWindowEx oder CreateWindow erzeugt, mit dem Namen der Steuerelement-Klasse als Parameter. Einige Standard-Steuerlemente haben spezifische Erzeugungs-Funktionen, aber auch diese sind nur Wrapper um CreateWindowEx um es einfacher zu machen, sie zu erzeugen. Existierende spezifische Erzeugungs-Funktionen sind wie folgt:

  • CreateToolbarEx
  • CreateStatusWindow
  • CreatePropertySheetPage
  • PropertySheet
  • ImageList_Create
Um Standard-Steuerlemente zu erzeugen, müssen Sie ihren Klassen-Namen kennen. Diese sind:


Klassen Name

Standard Steuerelement

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 und Property Pages und Image List Steuerelemente haben ihre eigenen Erzeugungs-Funktionen. Drag List Steuerelemente sind frisierte Listboxen, so dass sie nicht eine eigene Klasse haben. Das oben genannten Klassennamen sind überpüft indem ein Ressource-Skript das vom Visual C++ Ressource-Editor generiert wurde, gecheckt wurd. Sie differieren von den Klassennamen, die in Borland's Win32 API Referenz gelistet sind und Charles Petzold's Programming Windows 95. Die obige Liste ist die richtige.
Diese Standard-Steuerelemente können generelle Fenster-Stile wie WS_CHILD etc. benutzen. Sie haben auch ihre eigenen spezifizischen Stile, so wie TVS_XXXXX für Tree View Steuerelemente, LVS_xxxx für List View Steuerelemente control, etc. Die Win32 API Referenz ist in dieser Hinsicht Ihr bester Freund.
Nun, da wir wissen, wie Standard-Steuerelemente erzeugt werden, können wir mit den Kommunikationsmethoden zwischen Standard-Steuerelementen und ihren Eltern machen. Anders als bei Child-Fenster-Steuerelemente Standard-Steuerlemente kommunizieren mit ihren Eltern nicht via WM_COMMAND. Statt dessen senden sie eine WM_NOTIFY Nachricht an das Parent-Fenster, wenn interessante Ereignisse auftreten. Das Elter das Kind kontrollieren, indem es ihm Nachrichten sendet. Es gibt auch viele neue Nachrichten für diese neuen Steuerelemente. Sie sollten Ihre Win32 API Referenz für mehr Details konsultieren.
Lassen Sie uns die Progress Bar und die Status Bar im folgenden Beispiel untersuchen.

Beispiel Code:

.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 ; der Klassenname der Progress Bar Message db "Beendet!",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


Analyse:

invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT invoke ExitProcess,eax invoke InitCommonControls


Ich habe wohlbedacht InitCommonControls hinter ExitProcess plaziert um zu demonstrieren, dass InitCommonControls nur dafür da ist, die comctl32.dll in der Import-Sektion zu referenzieren. Wie Sie sehen können, laufen die Standard-Steuerlemente auch dann, wenn InitCommonControls nicht ausgeführt wird.

.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


Hier erzeugen wir die Standard-Steuerlemente. Beachten Sie, dass dieser CreateWindowEx Aufruf hWnd als das Eltern-Fenster-Handle enthält. Sie spezifziert auch eine Steuerlement-ID um das Steuerelement zu identifizieren. Wie auch immer, da wir das Steuerelement-Fenster-Handle haben, wird diese ID nicht benuzt. Alle Child-Fenster-Steuerlemente müssen den WS_CHILD Stil haben.

mov eax,1000 mov CurrentStep,eax shl eax,16 invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax invoke SendMessage,hwndProgress,PBM_SETSTEP,10,0


Nachdem die Progress Bar erzeugt wurde, können wir ihren Range setzen. Der Standard-Range ist von 0 bis 100. Wenn Sie damit nicht zufrieden sind, können Sie ihren eigenen Range mit der PBM_SETRANGE Nachricht setzen. lParam enthält bei dieser Nachricht den Range, der Maximum-Wert ist im oberen Word und das Minimum im unteren Word. Sie können mit der PBM_SETSTEP Nachricht spezifizieren, wie weit ein Schritt gehen soll. Im Beispiel sind es 10, was bedeutet, dass wenn die PBM_STEPIT Nachricht an die Progress Bar gesendet wird, der Fortschritts-Anzeiger um 10 erhöht wird. Sie können den Fortschritts-Anzeiger auch manuell setzen, indem Sie die PBM_SETPOS Nachricht senden. Diese Nachricht, gibt Ihnen mehr Kontrolle über die Progress Bar.

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


Als nächstes erzeugen wir eine Status-Bar, indem wir CreateStatusWindow aufrufen. Dieser Aufruf ist so einfach zu verstehen, dass ich ihn nicht kommtiere. Nachdem das Status-Fenster erzeugt wurde, erzeugen wir einen Timer. In diesem Beispiel, aktuallisieren wir die Progress Bar alle 100 ms, weshalb wir ein Timer Steuerelement benutzen müssen. Der Funktions-Prototyp von SetTimer:

SetTimer PROTO hWnd:DWORD, TimerID:DWORD, TimeInterval:DWORD, lpTimerProc:DWORD


hWnd : Eltern-Fenster Handle
TimerID : ein Timer-Identifizierer ungleich nuull. Sie können Ihren eigenen identifizierer erzeugen.
TimerInterval : das Timer Intervall in Millisekunden, welches vergehen muss, bevor der Timer die Timer-Prozedur aufruft oder eine WM_TIMER Nachricht sendet.
lpTimerProc : die Adresse der Timer-Funktion, die aufgerufen wird, wenn ein Intervall verstrichen ist. Wenn dieser Parameter gleich NULL ist, sendet der Timer statt dessen eine WM_TIMER Nachricht an das Eltern-Fenster.

Wenn der Aufruf erfolgreich war, wird die TimerID zurückgeliefert. Wenn er fehlgeschlagen ist, wird 0 zurückgeliefert. Das ist der Grund, warum der Identifizierer einen Wert ungleich 0 haben muss.

.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


Wenn das spezifzierte Intervall verstrichen ist, sendet der Timer eine WM_TIMER Nachricht. Sie fügen hier den Code, der ausgeführt werden soll, hier ein. In diesem Beispiel aktualisieren wir die Progress Bar und überprüfen um das maximum-Limit erreicht wurde. Wenn ja, killen wir den Timer und setzen den Text ins Status-Fenster mit der SB_SETTEXT Nachricht. Eine Message-Box erscheint und wenn der Benutzer OK klickt, löschen wir den Text in der Status Bar und die Progress Bar.


Deutsche Übersetzung: Joachim Rohde
Die original Win32Asm-Tutorials stammen von Iczelion's Win32 Assembly HomePage