Rozdział X - Okno dialogowe jako okno główne


Teraz nadeszła naprawdę interesująca część graficznego interfejsu użytkownika - okienko dialogowe. Na tej lekcji (oraz na następnej) nauczymy się, jak użyć okienka dialogowego w funkcji głównego okna.

 

obrazek

 

Załaduj pliki {przykładu 1} i {przykładu 2}

 

Teoria

Jeśli bawiłeś się wystarczająco długo przykładami z poprzedniej lekcji, to zapewne odkryłeś, iż nie można przenosić się z jednej kontrolki na drugą przy pomocy klawisza TAB. Jedyną metoda uaktywnienia danej kontrolki jest kliknięcie jej myszką. Sytuacja taka jest raczej uciążliwa. Inną rzeczą, którą mogłeś zauważyć, jest to, iż zmieniłem kolor tła okna nadrzędnego na szary zamiast zwykłego białego, jak w poprzednich przykładach. Celem była zgodność kolorów kontrolek z kolorem okna głównego. Istnieje sposób obejścia tego problemu, lecz nie jest on łatwy. Musiałbyś utworzyć wszystkie kontrolki okna potomnego jako podklasy swojego okna głównego.

Powodem istnienia tej niewygody jest to, iż kontrolki okna potomnego są pierwotnie zaprojektowane do pracy z oknem dialogowym, a nie ze zwykłym oknem. Standardowym kolorem kontrolek okien potomnych, takich jak przycisk, jest kolor szary, ponieważ obszar roboczy okna dialogowego jest zwykle szary, więc bez problemu pasują do takich okien nie wywołując potu na czole programisty.

Zanim zagłębimy się w szczegóły, powinniśmy się dowiedzieć, czym jest okno dialogowe. Nie jest to nic innego niż zwykłe okno zaprojektowane do pracy z kontrolkami okna potomnego. System Windows udostępnia również wewnętrznego "zarządcę okienek dialogowych", który odpowiada za większość logiki obsługi klawiatury, jak np. uaktywnianie następnych kontrolek, gdy użytkownik naciska klawisz TAB, naciskanie standardowego przycisku, jeśli zostanie wciśnięty klawisz Enter, itp. a wszystko po to, aby programista mógł zająć się zadaniami na wyższym poziomie. Okienka dialogowe są zwykle stosowane w funkcji narzędzi wejścia/wyjścia. Jako takie, okienko dialogowe można uważać za "czarną skrzynkę" wejścia/wyjścia, co oznacza, iż nie musisz wiedzieć, jak ono pracuje wewnętrznie, aby móc je używać. Musisz jedynie posiadać wiedzę, jak z nim współpracować. Jest to główna zasada programowania zorientowanego obiektowo (OOP) zwana ukrywaniem informacji. Jeśli czarna skrzynka została idealnie zaprojektowana, to użytkownik może z niej korzystać bez najmniejszej wiedzy na temat jej funkcjonowania. Problemem jest to, iż czarna skrzynka musi być doskonała, a to trudno osiągnąć w rzeczywistym świecie. Funkcje API biblioteki Win32 są również takimi czarnymi skrzynkami.

Trochę odbiegliśmy od tematu. Wracajmy do rzeczy. Okienka dialogowe opracowano, aby zmniejszyć wkład pracy programistów. Zwykle umieszczając kontrolki okna potomnego na zwykłym oknie musisz je utworzyć jako podklasę oraz samodzielnie zaprojektować logikę obsługi klawiatury. Lecz jeśli umieścisz je na oknie dialogowym, ono zajmie się ich obsługą. Musisz jedynie wiedzieć, jak odczytać z okna dialogowego wprowadzoną przez użytkownika informację oraz jak wysłać do niego polecenia.

Okno dialogowe definiowane jest jako zasób, tak samo jak menu. Tworzysz szablon okna dialogowego opisując jego charakterystyki oraz jego kontrolki, a następnie kompilujesz skrypt zasobów za pomocą kompilatora zasobów.

Zwróć uwagę, iż wszystkie zasoby są umieszczane razem w tym samym pliku skryptu zasobów. Możesz zastosować dowolny edytor tekstu do napisania szablonu okna dialogowego, lecz nie polecam tej metody. Powinieneś skorzystać z wizualnego edytora zasobów do aranżacji kontrolek okna potomnego w oknie dialogowym, ponieważ praca ta jest trudna do wykonania na piechotę. Dostępne jest kilkanaście doskonałych edytorów zasobów. Większość głównych zestawów kompilatorów zawiera własne edytory zasobów. Możesz wykorzystać je do utworzenia skryptu zasobów dla swojego programu, a następnie usunąć z nich nieistotne fragmenty.

Istnieją dwa główne rodzaje okien dialogowych: modalne i niemodalne. Niemodalne okno dialogowe pozwala ci uaktywnić inne okno. Przykładem może być okno dialogowe Znajdź w programie MS Word. Wśród okien modalnych wyróżniamy dwa podtypy: modalne dla aplikacji oraz modalne dla systemu. Okno dialogowe modalne dla aplikacji nie pozwoli ci uaktywnić innego okna należącego do tej samej aplikacji, lecz możesz uaktywnić okno w INNEJ aplikacji. Okno dialogowe modalne dla systemu nie pozwoli ci przejść do żadnego innego okna, zanim nie obsłużysz go.

Niemodalne okno dialogowe tworzone jest przez wywołanie funkcji API CreateDialogParam. Modalne okno dialogowe tworzy się wywołując DialogBoxParam. Rozróżnienie pomiędzy oknem dialogowym modalnym dla aplikacji i modalnym dla systemu dokonuje się jedynie poprzez styl DS_SYSMODAL. Jeśli dołączysz ten styl w szablonie okna dialogowego, to będzie ono oknem dialogowym modalnym dla systemu.

Możesz komunikować się z dowolną kontrolką okna potomnego w oknie dialogowym korzystając z funkcji SendDlgItemMessage. Jej składnia jest następująca:

SendDlgItemMessage PROTO hwndDlg:   DWORD,\ 
                         idControl: DWORD,\ 
                         uMsg:      DWORD,\ 
                         wParam:    DWORD,\ 
                         lParam:    DWORD


To wywołanie API jest niesamowicie użyteczne przy wymianie informacji z kontrolką okna potomnego. Na przykład jeśli chcesz pobrać tekst z kontrolki edycyjnej, możesz to zrobić tak:

INVOKE SendDlgItemMessage, hDlg,\
                           ID_EDITBOX,\
                           WM_GETTEXT, 256, ADDR text_buffer


Informację na temat możliwych do wysłania wiadomości znajdziesz w plikach pomocy dla funkcji API biblioteki Win32 (jak zwykle z resztą w takich przypadkach - pamiętaj o tym!).

System Windows udostępnia również kilka funkcji API specyficznych dla określonej kontrolki dla szybkiego ustawienia danych, na przykład GetDlgItemText, CheckDlgButton itp. Funkcje te są udostępniane dla wygody programisty, aby nie musiał szukać znaczenia wParam i lParam przy każdej wiadomości. Powinieneś z nich korzystać, gdy są dostępne, ponieważ funkcje takie ułatwiają późniejszą poprawę kodu aplikacji. Powróć do SendDlgItemMessage tylko wtedy, gdy dla danej kontrolki nie ma odpowiedniej funkcji API specyficznej dla niej.

Zarządca okienek dialogowych systemu Windows wysyła niektóre wiadomości do specjalizowanej funkcji zwrotnej zwanej procedurą okna dialogowego, która posiada następujący format:

DlgProc PROTO hDlg:   DWORD ,\ 
              iMsg:   DWORD ,\ 
              wParam: DWORD ,\ 
              lParam: DWORD


Procedura okna dialogowego jest bardzo podobna do procedury okna z wyjątkiem typu zwracanej wartości, która wynosi TRUE/FALSE zamiast LRESULT. Wewnętrzny zarządca okna dialogowego w systemie Windows JEST pełnoprawną procedurą okna dla okna dialogowego. Wywołuje on naszą procedurę okna dialogowego przy niektórych wiadomościach. Generalną zasadą jest to, iż jeżeli nasza procedura okna dialogowego obsługuje daną wiadomość, to MUSI zwrócić wartość TRUE w rejestrze eax, a jeśli tej wiadomości nie obsługuje, to w eax musi zwrócić FALSE. Zwróć uwagę, iż procedura okna dialogowego nie przekazuje nieobsługiwanych przez nią wiadomości dalej do wywołania DefWindowProc, ponieważ nie jest ona prawdziwą procedurą okna.

Istnieją dwa bezpośrednie wykorzystania okien dialogowych. Możesz je stosować jako okna główne swoich aplikacji lub używać ich do wprowadzania danych. Na tej lekcji zbadamy pierwsze podejście.

"Użycie okna dialogowego jako okna głównego" można zinterpretować dwojako.

  1. Możesz wykorzystać szablon okna dialogowego jako szablon klasy, którą rejestrujesz za pomocą wywołania RegisterClassEx. W tym przypadku okno dialogowe zachowuje się jak "normalne" okno: otrzymuje wiadomości poprzez procedurę okna, której adres zawarty jest w polu lpfnWndProc klasy okna, a nie poprzez procedurę okna dialogowego. Zaletą tego rozwiązania jest to, iż nie musisz sam tworzyć kontrolek okna potomnego, system Windows stworzy je dla ciebie przy tworzeniu okna dialogowego. Również Windows będzie obsługiwało logikę klawiatury, np. zmianę aktywnych kontrolek klawiszem TAB, itp. Dodatkowo możesz zdefiniować kursor myszki oraz ikonę swojego okna w strukturze klasy okna.
  2. Twój program po prostu tworzy okno dialogowe bez tworzenia jakiegokolwiek okna nadrzędnego. Takie podejście uwalnia nas od tworzenia pętli wiadomości, ponieważ wiadomości będą przesyłane bezpośrednio do procedury okna dialogowego. Nie musisz nawet rejestrować klasy okna!

Ta lekcja będzie dosyć długa. Przedstawię najpierw pierwsze rozwiązania, a później przyglądniemy się drugiemu.

Przykłady

Plik DIALOG.ASM - wersja nr 1.


.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 "DLGCLASS",0
MenuName   DB "MyMenu",0
DlgName    DB "MyDialog",0
AppName    DB "Nasze pierwsze okno dialogowe",0
TestString DB "Och! Jestem w edytorze",0

.DATA?

hInstance   HINSTANCE ?
CommandLine LPSTR     ?
buffer      DB 512 DUP(?)

.CONST

IDC_EDIT    EQU  3000
IDC_BUTTON  EQU  3001
IDC_EXIT    EQU  3002
IDM_GETTEXT EQU 32000
IDM_CLEAR   EQU 32001
IDM_EXIT    EQU 32002

.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 hDlg: 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, DLGWINDOWEXTRA
    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 CreateDialogParam, hInstance, ADDR DlgName, NULL, NULL, NULL
    mov    hDlg, eax
    INVOKE GetDlgItem, hDlg, IDC_EDIT
    INVOKE SetFocus, eax	
    INVOKE ShowWindow, hDlg, SW_SHOWNORMAL
    INVOKE UpdateWindow, hDlg

    .WHILE TRUE
        INVOKE GetMessage, ADDR msg, NULL, 0, 0
        .BREAK .IF (!eax)
        INVOKE IsDialogMessage, hDlg, ADDR msg
        .IF eax==FALSE
            INVOKE TranslateMessage, ADDR msg
            INVOKE DispatchMessage, ADDR msg
        .ENDIF
    .ENDW

    mov   eax, msg.wParam
    ret

WinMain ENDP

WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .IF uMsg==WM_CREATE
        INVOKE SetDlgItemText, hWnd, IDC_EDIT, ADDR AppName
    .ELSEIF uMsg==WM_DESTROY
        INVOKE PostQuitMessage, NULL
    .ELSEIF uMsg==WM_COMMAND
        mov eax, wParam
        .IF lParam==0
            .IF ax==IDM_GETTEXT
                INVOKE GetDlgItemText, hWnd, IDC_EDIT, ADDR buffer, 512
                INVOKE MessageBox, NULL, ADDR buffer, ADDR AppName, MB_OK
            .ELSEIF ax==IDM_CLEAR
                INVOKE SetDlgItemText, hWnd, IDC_EDIT, NULL
            .ELSE
                INVOKE DestroyWindow, hWnd
            .ENDIF
        .ELSE
            mov dx, WORD PTR wParam + 2
            .IF dx==BN_CLICKED
                .IF ax==IDC_BUTTON
                    INVOKE SetDlgItemText, hWnd, IDC_EDIT, ADDR TestString
                .ELSEIF ax==IDC_EXIT
                    INVOKE SendMessage, hWnd, WM_COMMAND, IDM_EXIT, 0
                .ENDIF
            .ENDIF
        .ENDIF
    .ELSE
        INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam
        ret
    .ENDIF

    xor eax, eax
    ret
   
WndProc ENDP

END start

 

Plik DIALOG.RC

#include "resource.h"

#define IDC_EDIT     3000
#define IDC_BUTTON   3001
#define IDC_EXIT     3002

#define IDM_GETTEXT 32000
#define IDM_CLEAR   32001
#define IDM_EXIT    32003


MyDialog DIALOG 10, 10, 205, 60

STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
      WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME |
      DS_3DLOOK

CAPTION "Nasze pierwsze okno dialogowe"

CLASS "DLGCLASS"
{
    EDITTEXT       IDC_EDIT, 15, 17, 111, 13, ES_AUTOHSCROLL |
                   ES_LEFT | WS_TABSTOP
    DEFPUSHBUTTON  "Przywitaj się", IDC_BUTTON, 141, 10, 52, 13
    PUSHBUTTON     "&Koniec", IDC_EXIT, 141, 26, 52, 13
}

MyMenu     MENU
{
    POPUP "Testowe Kontrolki"
    {
        MENUITEM "Pobierz tekst", IDM_GETTEXT
        MENUITEM "Wymaż tekst", IDM_CLEAR
        MENUITEM SEPARATOR
        MENUITEM "&Koniec", IDM_EXIT
    }
}

Analiza

Przeanalizujmy pierwszy przykład.

Pokazuje on sposób rejestracji szablonu okna dialogowego jako klasy okna i utworzenia okna z tej klasy. Upraszcza to twój program, ponieważ nie musisz własnoręcznie tworzyć kontrolki okna potomnego.

Najpierw przeanalizujmy szablon okna dialogowego.

MyDialog DIALOG 10, 10, 205, 60


Zadeklaruj nazwę okna dialogowego, w tym przypadku "MyDialog", za którą umieść słowo kluczowe DIALOG. Następne cztery liczby określają współrzędne x i y oraz szerokość i wysokość okna dialogowego podane w jego jednostkach (to nie to samo co piksele).

    STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
          WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME |
          DS_3DLOOK


Zadeklaruj style okienka dialogowego.

    CAPTION "Nasze pierwsze okno dialogowe"


To jest tekst, który pojawi się na pasku tytułowym okienka dialogowego.


    CLASS "DLGCLASS"


Ten wiersz jest najistotniejszy. Słowo kluczowe CLASS umożliwia nam użycie szablonu okna dialogowego jako klasy okna. Za tym słowem występuje nazwa "klasy okna".

    {
        EDITTEXT       IDC_EDIT, 15, 17, 111, 13, ES_AUTOHSCROLL |
                       ES_LEFT | WS_TABSTOP
        DEFPUSHBUTTON  "Przywitaj się", IDC_BUTTON, 141, 10, 52, 13
        PUSHBUTTON     "&Koniec", IDC_EXIT, 141, 26, 52, 13
    }


Powyższy blok definiuje kontrolki okna dialogowego. Składnia jest zwykle następująca:


typ-kontrolki "tekst" ,numerID, x, y, szerokość, wysokość [,style]


Typy kontrolek są określone przez stałe kompilatora zasobów, zatem musisz skonsultować się z jego instrukcją.

Teraz przechodzimy do kodu źródłowego w asemblerze. Interesujący fragment występuje w strukturze klasy okna:

    mov wc.cbWndExtra, DLGWINDOWEXTRA
    ...
    mov wc.lpszClassName, OFFSET ClassName


Zwykle pole to pozostawia się z wartością NULL, lecz jeżeli chcemy zarejestrować szablon okna dialogowego jako klasę okna, musimy ustawić jego wartość na DLGWINDOWEXTRA. Zwróć uwagę, iż nazwa tej klasy musi być identyczna z nazwą za słowem kluczowym CLASS w szablonie okna dialogowego. Pozostałe pola struktury klasy okna są inicjowane standardowo. Po wypełnieniu odpowiednią informacja struktury klasy okna rejestrujemy ją przy pomocy funkcji RegisterClassEx. Znajome? To jest ta sama procedura, którą musisz wykonać w celu zarejestrowania zwykłej klasy okna.

    INVOKE CreateDialogParam, hInstance, ADDR DlgName, NULL, NULL, NULL


Po zarejestrowaniu "klasy okna" tworzymy nasze okno dialogowe. W tym przykładzie tworzę je jako niemodalne za pomocą funkcji CreateDialogParam. Funkcja ta wymaga pięciu parametrów, lecz wypełnić musisz tylko pierwsze dwa: uchwyt egzemplarza programu oraz wskazanie nazwy szablonu okna dialogowego. Zwróć uwagę, iż drugi parametr nie jest wskazaniem nazwy klasy.

W tym miejscu system Windows tworzy okno dialogowe wraz z jego kontrolkami. Twoja procedura okna otrzyma jak zwykle wiadomość WM_CREATE.

    INVOKE GetDlgItem, hDlg, IDC_EDIT
    INVOKE SetFocus, eax


Po utworzeniu okna dialogowego chcę, aby aktywnym elementem była kontrolka edycyjna. Gdybym umieścił ten kod w sekcji WM_CREATE, to wywołanie GetDlgItem by się nie powiodło, ponieważ w tym czasie kontrolki okna dialogowego nie są jeszcze utworzone. Jedynym sposobem osiągnięcia założonego celu jest wywołanie tej funkcji po utworzeniu okna dialogowego wraz ze wszystkimi jego kontrolkami. Funkcja GetDlgItem pobiera numer ID kontrolki i zwraca powiązany z nim uchwyt okna kontrolki. W ten sposób możesz otrzymać uchwyt okna, jeśli znasz jego numer ID.


    .WHILE TRUE
        INVOKE GetMessage, ADDR msg, NULL, 0, 0
        .BREAK .IF (!eax)
        INVOKE IsDialogMessage, hDlg, ADDR msg
        .IF eax==FALSE
            INVOKE TranslateMessage, ADDR msg
            INVOKE DispatchMessage, ADDR msg
        .ENDIF
    .ENDW


Program wchodzi w pętlę wiadomości i zanim przetłumaczymy i wyślemy wiadomości, wywołujemy funkcję IsDialogMessage, aby zarządca okienka dialogowego mógł obsłużyć dla nas logikę naszego okna dialogowego. Jeśli funkcja ta zwróci wartość TRUE, to oznacza to, iż wiadomość była przeznaczona dla okna dialogowego i została obsłużona przez jego zarządcę. Zwróć uwagę na jeszcze jedną różnicę w stosunku do poprzednich lekcji. Gdy procedura okna chce pobrać tekst z pola edycyjnego, wywołuje funkcję GetDlgItemText zamiast GetWindowText. Funkcja GetDlgItemText akceptuje numer ID kontrolki zamiast uchwytu okna. Ułatwia to wywołanie w przypadku okna dialogowego.

Teraz przejdźmy do drugiego przykładu wykorzystania okna dialogowego jako głównego okna aplikacji. W następnym przykładzie utworzę okienko dialogowe modalne dla aplikacji. Nie znajdziesz pętli wiadomości ani procedury okna, ponieważ są one zbędne!

Plik DIALOG.ASM - wersja nr 2.


.386

.MODEL FLAT, STDCALL

OPTION CASEMAP:NONE

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

DlgProc PROTO :DWORD, :DWORD, :DWORD, :DWORD

.DATA

DlgName    DB "MyDialog",0
AppName    DB "Nasze drugie okno dialogowe",0
TestString DB "Och! Jestem w edytorze",0

.DATA?

hInstance   HINSTANCE ?
CommandLine LPSTR     ?
buffer      DB 512 DUP(?)

.CONST

IDC_EDIT    EQU  3000
IDC_BUTTON  EQU  3001
IDC_EXIT    EQU  3002
IDM_GETTEXT EQU 32000
IDM_CLEAR   EQU 32001
IDM_EXIT    EQU 32002


.CODE

start:
    INVOKE GetModuleHandle, NULL
    mov    hInstance, eax
    INVOKE DialogBoxParam, hInstance,\
                           ADDR DlgName, NULL, ADDR DlgProc, NULL
    INVOKE ExitProcess, eax

DlgProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .IF uMsg==WM_INITDIALOG
        INVOKE GetDlgItem, hWnd, IDC_EDIT
        INVOKE SetFocus, eax
    .ELSEIF uMsg==WM_CLOSE
        INVOKE SendMessage, hWnd, WM_COMMAND, IDM_EXIT, 0
    .ELSEIF uMsg==WM_COMMAND
        mov eax, wParam
        .IF lParam==0
            .IF ax==IDM_GETTEXT
                INVOKE GetDlgItemText, hWnd, IDC_EDIT, ADDR buffer, 512
                INVOKE MessageBox, NULL, ADDR buffer, ADDR AppName, MB_OK
            .ELSEIF ax==IDM_CLEAR
                INVOKE SetDlgItemText, hWnd, IDC_EDIT, NULL
            .ELSEIF ax==IDM_EXIT
                INVOKE EndDialog, hWnd, NULL
            .ENDIF
        .ELSE
            mov dx, WORD PTR wParam + 2
            .IF dx==BN_CLICKED
                .IF ax==IDC_BUTTON
                    INVOKE SetDlgItemText, hWnd, IDC_EDIT, ADDR TestString
                .ELSEIF ax==IDC_EXIT
                    INVOKE SendMessage, hWnd, WM_COMMAND, IDM_EXIT, 0
                .ENDIF
            .ENDIF
        .ENDIF
    .ELSE
        mov eax, FALSE
        ret
    .ENDIF
    mov eax, TRUE
    ret
	
DlgProc ENDP

END start

 

Plik DIALOG.RC - wersja nr 2

 

#include "resource.h"

#define IDR_MENU1    3003
#define IDC_EDIT     3000
#define IDC_BUTTON   3001
#define IDC_EXIT     3002

#define IDM_GETTEXT 32000
#define IDM_CLEAR   32001
#define IDM_EXIT    32003

MyDialog DIALOG 10, 10, 205, 60

STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
      WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK

CAPTION "Nasze drugie okno dialogowe"

MENU IDR_MENU1

{
    EDITTEXT      IDC_EDIT, 15, 17, 111, 13, ES_AUTOHSCROLL | ES_LEFT
    DEFPUSHBUTTON "CZEŚĆ", IDC_BUTTON, 141, 10, 52, 13
    PUSHBUTTON    "&Koniec", IDC_EXIT, 141, 26, 52, 13
}

IDR_MENU1 MENU
{
    POPUP "Kontrolki Testowe"
    {
        MENUITEM "Pobierz tekst", IDM_GETTEXT
        MENUITEM "Wyczyść tekst", IDM_CLEAR
        MENUITEM SEPARATOR
        MENUITEM "&Koniec", IDM_EXIT
    }
}


Analizujemy dalej:


DlgProc PROTO :DWORD, :DWORD, :DWORD, :DWORD


Deklarujemy prototyp funkcji dla DlgProc, aby można było się do niej odwołać za pomocą operatora ADDR w wierszu poniżej:


    INVOKE DialogBoxParam, hInstance,\
                           ADDR DlgName, NULL, ADDR DlgProc, NULL


Powyższy wiersz wywołuje funkcję DialogBoxParam, która przyjmuje pięć parametrów: uchwyt egzemplarza programu, nazwę szablonu okna dialogowego, uchwyt okna nadrzędnego, adres procedury okna dialogowego oraz dane specyficzne dla tego okna. DialogBoxParam tworzy modalne okno dialogowe. Nie zwróci ona sterowania, aż okno dialogowe zostanie zamknięte.


    .IF uMsg==WM_INITDIALOG
        INVOKE GetDlgItem, hWnd, IDC_EDIT
        INVOKE SetFocus, eax
    .ELSEIF uMsg==WM_CLOSE
        INVOKE SendMessage, hWnd, WM_COMMAND, IDM_EXIT, 0


Procedura okna dialogowego jest podobna do procedury okna, z tą różnicą, iż nie otrzymuje wiadomości WM_CREATE. Pierwszą otrzymywaną wiadomością jest WM_INITDIALOG. Zwykle w obsłudze tej wiadomości umieszczamy kod inicjalizacji. Zauważ, iż musisz umieścić w rejestrze eax wartość TRUE, jeśli przetwarzasz tę wiadomość.

Wewnętrzny zarządca okienka dialogowego nie wysyła automatycznie do naszej procedury okna dialogowego wiadomości WM_DESTROY przy wysłaniu WM_CLOSE. Zatem jeśli chcemy zareagować, gdy użytkownik kliknie myszką przycisk zamykający na naszym okienku dialogowym, musimy obsłużyć wiadomość WM_CLOSE. W naszym przykładzie wysyłamy wiadomość WM_COMMAND z wartością IDM_EXIT w wParam. Ma to ten sam efekt, co wybranie elementu menu Koniec. W odpowiedzi wywołana zostaje funkcja EndDialog,

Obsługa wiadomości WM_COMMAND pozostaje taka sama.

Gdy chcesz usunąć okno dialogowe, jedynym sposobem jest wywołanie funkcji EndDialog. Nie próbuj stosować DestroyWindow! Funkcja EndDialog nie usuwa okna dialogowego natychmiast. Ustawia ona jedynie znacznik dla wewnętrznego zarządcy okienek dialogowych i powraca do wykonania następnej instrukcji.

Teraz przebadajmy plik zasobów. Widoczną zmianą jest to, iż zamiast użycia tekstu jako nazwy menu używamy wartość IDR_MENU1. Jest to konieczne, jeśli chcesz dołączyć menu do okna dialogowego utworzonego przy pomocy DialogBoxParam. Zwróć uwagę, iż w szablonie okna dialogowego musisz dodać słowo kluczowe MENU, za którym występuje numer zasobu ID.

Drugą widoczną różnicą jest brak w drugim przykładzie ikony dla okna dialogowego. Jednakże możesz ustawić tę ikonę wysyłając do okna dialogowego wiadomość WM_SETICON podczas obsługi wiadomości WM_INITDIALOG. W tym celu dodaj do tej sekcji następujący kod:


    .IF uMsg==WM_INITDIALOG
        INVOKE GetDlgItem, hWnd, IDC_EDIT
        INVOKE SetFocus, eax
        INVOKE LoadIcon, NULL, IDI_APPLICATION
        INVOKE SendMessage, hWnd, WM_SETICON, ICON_SMALL, eax

 

Dodatek w Pascalu

Ta sama aplikacja w Pascalu.

Uwaga

Program używa tego samego pliku zasobów i pliku definicji, co wersja asemblerowa. Musisz je przekopiować do katalogu uruchomieniowego. Plik zasobów nazwij RSRC.RC (można zachować oryginalną nazwę pliku. ale wtedy należy ustawić ją w opcjach projektu).

Oryginalny kompilator zasobów w pakiecie DevPascal (windres.exe) ma błąd, który uniemożliwi poprawną kompilację. Dlatego najlepszym rozwiązaniem będzie skopiowanie tego pliku z pakietu DevC++, który również możesz darmowo pobrać z sieci. Kompilator windres.exe znajduje się w obu pakietach w katalogu bin.

Wersja nr 1 (wymaga pierwszego pliku zasobów)

 

{********************************
**  I Liceum Ogólnokształcące  **
**           w Tarnowie        **
**       mgr Jerzy Wałaszek    **
********************************}

program MainDialog1;

uses Windows;

const
  ClassName   = 'DLGCLASS';
  MenuName    = 'MyMenu';
  DlgName     = 'MyDialog';
  AppName     = 'Nasze pierwsze okno dialogowe';
  TestString  = 'Och! Jestem w edytorze';
  IDC_EDIT    =  3000;
  IDC_BUTTON  =  3001;
  IDC_EXIT    =  3002;
  IDM_GETTEXT = 32000;
  IDM_CLEAR   = 32001;
  IDM_EXIT    = 32002;

var
  buffer : array [0..511] of byte;

function WndProc(hWnd:HWND;uMsg:UINT;wParam:WPARAM;lParam:LPARAM) : longint;
begin
  WndProc := 0;
  case uMsg of
    WM_CREATE  : SetDlgItemText(hWnd,IDC_EDIT,AppName);
    WM_DESTROY : PostQuitMessage(0);
    WM_COMMAND : if lParam = 0 then
                   case (wParam and $ffff) of
                     IDM_GETTEXT : begin   
                                     GetDlgItemText(hWnd,IDC_EDIT,@buffer,512);
                                     MessageBox(0,@buffer,AppName,MB_OK);
                                   end;
                     IDM_CLEAR   : SetDlgItemText(hWnd,IDC_EDIT,0);
                     else DestroyWindow(hWnd);
                   end
                 else
                 begin
                   if (wParam shr 16) = BN_CLICKED then
                     case (wParam and $ffff) of
                       IDC_BUTTON : SetDlgItemText(hWnd,IDC_EDIT,TestString);
                       IDC_EXIT   : SendMessage(hWnd,WM_COMMAND,IDM_EXIT,0);
                     end;
                 end;
    else {dla case}
      WndProc := DefWindowProc(hWnd,uMsg,wParam,lParam);
  end;
end;

function WinMain(hInst,hPrevInst:HINST;CmdLine:LPSTR;CmdShow:DWORD) : longint;

var
  wc   : WNDCLASSEX;
  msg  : MSG;
  hDlg : HWND;

begin
  wc.cbSize        := SizeOf(WNDCLASSEX);
  wc.style         := CS_HREDRAW or CS_VREDRAW;
  wc.lpfnWndProc   := @WndProc;
  wc.cbClsExtra    := 0;
  wc.cbWndExtra    := DLGWINDOWEXTRA;
  wc.hInstance     := hInst;
  wc.hbrBackground := COLOR_BTNFACE + 1;
  wc.lpszMenuName  := MenuName;
  wc.lpszClassName := ClassName;
  wc.hIcon         := LoadIcon(0,IDI_APPLICATION);
  wc.hIconSm       := wc.hIcon;
  wc.hCursor       := LoadCursor(0,IDC_ARROW);
  RegisterClassEx(wc);
  hDlg := CreateDialogParam(hInst,DlgName,0,Nil,0);
  SetFocus(GetDlgItem(hDlg,IDC_EDIT));
  ShowWindow(hDlg,SW_SHOWNORMAL);
  UpdateWindow(hDlg);
  while GetMessage(msg,0,0,0) do
    if not IsDialogMessage(hDlg,msg) then
    begin
      TranslateMessage(msg);
      DispatchMessage(msg);
    end;
  WinMain := msg.wParam;
end;

begin
  ExitProcess(WinMain(GetModuleHandle(0),0,GetCommandLine,SW_SHOWDEFAULT));
end.

 

Wersja nr 2 (wymaga drugiego pliku zasobów)

 

{********************************
**  I Liceum Ogólnokształcące  **
**           w Tarnowie        **
**       mgr Jerzy Wałaszek    **
********************************}

program MainDialog2;

uses Windows;

const
  DlgName     = 'MyDialog';
  AppName     = 'Nasze drugie okno dialogowe';
  TestString  = 'Och! Jestem w edytorze';
  IDC_EDIT    =  3000;
  IDC_BUTTON  =  3001;
  IDC_EXIT    =  3002;
  IDM_GETTEXT = 32000;
  IDM_CLEAR   = 32001;
  IDM_EXIT    = 32002;

var
  buffer : array[0..511] of byte;

function DlgProc(hWnd:HWND;uMsg:UINT;wParam:WPARAM;lParam:LPARAM) : longint;
begin
  DlgProc := longint(true);
  case uMsg of
    WM_INITDIALOG : SetFocus(GetDlgItem(hWnd,IDC_EDIT));
    WM_CLOSE      : SendMessage(hWnd,WM_COMMAND,IDM_EXIT,0);
    WM_COMMAND    : if lParam = 0 then
                      case (wParam and $ffff) of
                        IDM_GETTEXT: begin
                                       GetDlgItemText(hWnd,IDC_EDIT,@buffer,512);
                                       MessageBox(0,@buffer,AppName,MB_OK);
                                     end;
                        IDM_CLEAR  : SetDlgItemText(hWnd,IDC_EDIT,0);
                        IDM_EXIT   : EndDialog(hWnd,0);
                      end
                    else if (wParam shr 16) = BN_CLICKED then
                    begin
                      if (wParam and $ffff) = IDC_BUTTON then
                        SetDlgItemText(hWnd,IDC_EDIT,TestString)
                      else if (wParam and $ffff) = IDC_EXIT then
                       SendMessage(hWnd,WM_COMMAND,IDM_EXIT,0);
                    end;
    else {dla case}
      DlgProc := longint(false);
  end;
end;

begin
  ExitProcess(DialogBoxParam(GetModuleHandle(0),DlgName,0,@DlgProc,0));
end.

 

Autorem kursu jest Iczelion. Kurs programowania Windows znalazł się na serwerze I LO w Tarnowie za pisemną zgodą autora.
Tłumaczenie z języka angielskiego, opracowanie HTML i konwersję przykładów programów wykonał mgr Jerzy Wałaszek.


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2024 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe