Iczelion - 09 - Child-Fenster Steuerelemente

Tutorial 9: Child-Fenster Steuerelemente


In diesem Tutorial werden wir die Child-Fenster Steuerelemente erforschen, welche sehr wichtige Ein- und Ausgabegeräte für unsere Programme sind.

Laden Sie hier das Beispiel herunter.

Theorie:

Windows unterstützt verschieden vordefinierte Fensterklassen, welche wir fertig in unser Programm benutzen können. Die meiste Zeit werden wir sie als Komponenten einer Dialog-Box benutzen, so dass sie Child-Fenster Steuerelemente genannt werden (child=Kind). Die Child-Fenster Steuerelemente bearbeiten ihre eigenen Maus und Tastatur Eingaben und benachrichtigen das Eltern-Fenster wenn sich ihr Status ändert. Sie erleichtern die Bürde des Programmierers enorm, weshalb Sie sie so oft wie möglich verwenden sollten. In diesem Tutorial werde ich sie in ein normales Fenster plazieren, nur um zu demonstrieren, wie Sie sie erzeugen und benutzen können, aber in Wirklichkeit sollten Sie sie in eine Dialog-Box plazieren.

Beispiele vordefinierter Fenster-Klassen sind Button, ListBox, CheckBox, Radio Button, Edit, etc.

Um ein Child-Fenster Steuerelement nutzen zu können, müssen Sie es mit CreateWindow oder CreateWindowEx erzeugen. Beachten Sie, dass Sie die Fenster-Klasse nicht registrieren brauchen, da es für Sie schon von Windows registriert wurde. Der Klassen-Name Parameter MUSS der vordefinierte Klassen-Name sein. Sagen wir, Sie wollen ein Button erzeugen, dann müssen Sie als Klassen-Name von CreateWindowEx "button" angeben. Die anderen Parameter die Sie füllen müssen, sind das Eltern-Fenster Handle und die Steuerelement ID. Die Steuerelement ID muss einmalig unter den Steuerelementen sein. Die Steuerlement ID ist die ID des Steuerelementes. Sie können sie benutzen um zwischen den einzelnen Steuerelementen zu unterscheiden.

Nachdem das Steuerelement erzeugt wurde, wird es Nachrichten an sein Eltern-Fenster senden um ihnen mitzuteilen wenn sich sein Zustand geändert hat. Normalerweise erzeugen Sie Child-Fenster während der WM_CREATE Nachricht des Eltern-Fenster. Das Child-Fenster sendet WM_COMMAND Nachrichten an das Eltern-Fenster, mit seiner Steuerelement ID in dem unteren Word von wParam, den Benachrichtigungscode in dem oberen Word von wParam und sein Fenster-Handle in lParam. Jedes Child-Fenster Steuerelement hat verschieden Benachrichtigungs-Codes, ziehen Sie ihre Win32 API Referenz für mehr Informationen zu rate.

Das Eltern-Fenster kann auch Nachrichten senden, an das Child-Fenster, mittels der SendMessage Funktion. Die SendMessage Funktion sendet die spezifizierte Nachricht mit den entsprechenden Werten in wParam und lParam an das Fenster, dass in dem Fenster-Handle spezifiziert wird. Es ist eine extrem nützliche Funktion, da Sie Nachrichten an jedes Fenster senden können, dessen Handle Sie kennen.

So, nachdem das Child-Fenster erzeugt wurde, das Eltern-Fenster muss die WM_COMMAND Nachrichten abarbeiten um auf Benachrichtigungs-Codes des Child-Fensters reagieren zu können.

Beispiel:

Wir werden ein Fenster erzeugen, welches ein Edit-Steuerelement und einen Pushbutton enthält. Wenn Sie den Button anklicken, wird eine Message-Box erscheinen, die den Text anzeigt, den Sie in die Edit-Box eingegeben haben. Es gibt auch 4 Menü-Elemente:

  1. Say Hello
  2.   -- Schreibt einen String in die Edit-Box
  3. Clear Edit Box
  4. -- Löscht den Inhalt der Edit-Box
  5. Get Text
  6. -- Zeigt eine Message-Box mit dem Text der Edit-Box an
  7. Exit
  8. -- Schließt das Programm.
.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 um den empfangen Text aus der Edit-Box zu speichern .const ButtonID equ 1 ; Die Kontroll-ID des Button Steuerelements EditID equ 2 ; Die Kontroll-ID des Edit Steuerelements 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

Analyse:

Lassen Sie uns das Programm analysieren.

.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
Wir erzeugen die Steuerelement während der Bearbeitung der WM_CREATE-Nachricht. Wir rufen CreateWindowEx mit einem extra Fenster-Stil, WS_EX_CLIENTEDGE auf, welches die Client-Area abgesenkt erscheinen lässt. Der Name von jedem Steuerelement ist ein vordefinierter, "edit" für ein Edit-Steuerelement, "button" für ein Button-Steuerelement. Als nächstes spezifizieren wir die Child-Fenster-Stile. Jedes Steuerelement hat ein extra Stil zu dem normalen Fenster-Stil. Die müssen Sie in der Win32-API Referenz nachschlagen. Beachten Sie, dass Sie eine Steuerelement ID anstatt eines Menü-Handles angeben. Das verursacht keinen Nachteil, da ein Child-Fenster Steuerelement kein Menü haben kann.

Nach dem erzeugen jedes Elementes, speichern wir sein Handle in einer Variablen für den späteren Gebrauch.

SetFocus wird aufgerufen, um den Fokus auf die Edit-Box zu setzen, so dass der Benutzer sofort Text eingeben kann.

Nun kommt der wirklich spannende Teil. Jedes Child-Fenster Steuerelement sendet Bemerkungen an sein Eltern-Fenster mit WM_COMMAND.

.ELSEIF uMsg==WM_COMMAND mov eax,wParam .IF lParam==0


Erinnern Sie sich daran, dass ein Menü auch eine WM_COMMAND-Nachricht sendet, um das Fenster über seinen Status zu informieren. Wie können Sie zwischen WM_COMMAND-Nachrichten unterscheiden, ob die von einem Menü oder einem Steuerelement kommt? Unten ist die Antwort.

  Unteres Word von wParam Oberes Word von wParam lParam
Menü Menü ID 0 0
Steuerelement Steuerelement ID Benachrichtigungs-Code Child Fenster Handle


Wie Sie sehen können, sollten sie lParam überprüfen. Wenn es gleich Null ist, ist die aktuelle WM_COMMAND Nachricht von einem Menü. Sie können nicht wParam benutzen um zwischen einem Menü und einem Steuerelement zu unterscheiden, da Menü-ID und Steuerelement ID identisch sein können und der Benachrichtigungs-Code Null sein kann.

.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


Sie können einen String in die Edit Box bringen, indem Sie SetWindowText aufrufen. Sie löschen den Inhalt einer Edit-Box indem Sie SetWindowText mit NULL aufrufen. SetWindowText ist eine Allzweck-API-Funktion. Sie können SetWindowText benutzen um die Beschriftung eines Fensters oder den Text eines Buttons zu ändern.

Um den Text in einer Edit-Box zu erhalten, benutzen Sie GetWindowText.

.IF ax==ButtonID shr eax,16 .IF ax==BN_CLICKED invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0 .ENDIF .ENDIF


Der obere Code-Ausschnitt behandelt die Bedingungen, wenn der Benutzer ein Button gedrückt hat. Als erstes wird das untere Word von wParam überprüft, um zu sehen ob die Steuerelement ID auf den Button zutrifft. Wenn dem so ist, wird ob das obere Word von wParam überprüft, ob der Benachrichtigungs-Code BN_CLICKED ist, welcher gesendet wird, wenn ein Button geklickt wurde.

Der interessante Teil kommt nachdem sicher ist, dass der Benachrichtigungs-Code BN_CLICKED ist. Wir wollen den Text aus der Edit-Box in einer Message Box darstellen. Wir können den Code in der IDM_GETTEXT-Sektion oben duplizieren, aber das macht keinen Sinn. Wenn wir irgendwie eine WM_COMMAND-Nachricht mit IDM_GETTEXT als Wert im unteren Word von wParam an unsere eigene Fenster-Prozedur senden können, können wird ein Duplizieren vermeiden und unser Programm vereinfachen. Die SendMessage Funktion ist die Antwort. Diese Funktion sendet jegliche Nachricht zu jeglichem Fenster mit wParam und lParam, das wir wollen. Statt den Code zu duplizieren, rufen wir SendMessage mit dem Eltern-Fenster Handle, WM_COMMAND, IDM_GETTEXT und 0 auf. Das hat den selben Effekt als wenn man 'Get Text' aus dem Menü wählen würden. Die Fenster-Prozedur merkt keinen Unterschied zwischen den beiden.

Sie sollten diese Technik so häufig wie möglich verwenden, um ihren Code mehr zu organisieren.

Zu guter Letzt vergessen Sie nicht die TranslateMessage-Funktion in der Nachrichtenschleife aufzurufen. Da Sie etwas Text in die Edit-Box tippen müssen, muss ihr Programm die reinen Tastatur-Eingaben in lesbaren Text übersetzen. Wenn Sie diese Funktion weglassen, werden Sie nicht in der Lage sein, irgend etwas in ihre Edit-Box einzugeben.


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