Udostępnij przez


Używanie okien dialogowych

Okna dialogowe służą do wyświetlania informacji i monitowania o podanie danych wejściowych od użytkownika. Aplikacja ładuje i inicjuje okno dialogowe, przetwarza dane wejściowe użytkownika i niszczy okno dialogowe po zakończeniu zadania przez użytkownika. Sposób obsługi okien dialogowych różni się w zależności od tego, czy okno dialogowe jest modalne, czy niemodalne. Modalne okno dialogowe wymaga, aby użytkownik zamknął okno dialogowe przed aktywowaniem innego okna w aplikacji. Użytkownik może jednak aktywować okna w różnych aplikacjach. Okno dialogowe bez moderowania nie wymaga natychmiastowej odpowiedzi od użytkownika. Jest on podobny do głównego okna zawierającego kontrolki.

W poniższych sekcjach omówiono sposób używania obu typów okien dialogowych.

Wyświetlanie pola komunikatu

Najprostszą formą modalnego okna dialogowego jest okno komunikatu. Większość aplikacji używa pól komunikatów, aby ostrzegać użytkownika o błędach i monitować o wskazówki dotyczące sposobu kontynuowania po wystąpieniu błędu. Okno komunikatu można utworzyć przy użyciu funkcji MessageBox lub MessageBoxEx, określając komunikat oraz liczbę i typ przycisków do wyświetlenia. System tworzy modalne okno dialogowe, udostępniając własny szablon okna dialogowego i procedurę. Gdy użytkownik zamknie okno komunikatu, messageBox lub MessageBoxEx zwraca wartość identyfikującą przycisk wybrany przez użytkownika, aby zamknąć okno komunikatu.

W poniższym przykładzie aplikacja wyświetla pole komunikatu z monitem użytkownika o akcję po wystąpieniu warunku błędu. W polu komunikatu zostanie wyświetlony komunikat opisujący warunek błędu i sposób jego rozwiązania. Styl MB_YESNO kieruje MessageBox, aby wyświetlić dwa przyciski, dzięki którym użytkownik może zdecydować, jak postępować.

int DisplayConfirmSaveAsMessageBox()
{
    int msgboxID = MessageBox(
        NULL,
        L"temp.txt already exists.\nDo you want to replace it?",
        L"Confirm Save As",
        MB_ICONEXCLAMATION | MB_YESNO
    );

    if (msgboxID == IDYES)
    {
        // TODO: add code
    }

    return msgboxID;    
}

Na poniższej ilustracji przedstawiono dane wyjściowe z poprzedniego przykładu kodu:

okno komunikatu

Tworzenie modalnego okna dialogowego

Okno dialogowe można utworzyć przy użyciu funkcji DialogBox. Należy określić identyfikator lub nazwę zasobu szablonu okna dialogowego i wskaźnik do procedury okna dialogowego. Funkcja DialogBox ładuje szablon, wyświetla okno dialogowe i przetwarza wszystkie dane wejściowe użytkownika, dopóki użytkownik nie zamknie okna dialogowego.

W poniższym przykładzie aplikacja wyświetla modalne okno dialogowe, gdy użytkownik kliknie Usuń element z menu aplikacji. Okno dialogowe zawiera kontrolkę edycji (w której użytkownik wprowadza nazwę elementu) oraz przyciski OK i Anuluj. Identyfikatory kontrolek dla tych kontrolek są odpowiednio ID_ITEMNAME, IDOK i IDCANCEL.

Pierwsza część przykładu składa się z instrukcji, które tworzą modalne okno dialogowe. Te polecenia w procedurze okna dla głównego okna aplikacji tworzą okno dialogowe, gdy system odbiera komunikat WM_COMMAND o identyfikatorze menu IDM_DELETEITEM. Drugą częścią przykładu jest procedura okna dialogowego, która pobiera zawartość kontrolki edycji i zamyka okno dialogowe po otrzymaniu komunikatu WM_COMMAND.

Poniższe instrukcje tworzą modalne okno dialogowe. Szablon okna dialogowego jest zasobem w pliku wykonywalnym aplikacji i ma identyfikator zasobu DLG_DELETEITEM.

case WM_COMMAND: 
    switch (LOWORD(wParam)) 
    { 
        case IDM_DELETEITEM: 
            if (DialogBox(hinst, 
                          MAKEINTRESOURCE(DLG_DELETEITEM), 
                          hwnd, 
                          (DLGPROC)DeleteItemProc)==IDOK) 
            {
                // Complete the command; szItemName contains the 
                // name of the item to delete. 
            }

            else 
            {
                // Cancel the command. 
            } 
            break; 
    } 
    return 0L; 

W tym przykładzie aplikacja określa główne okno jako okno właściciela okna dialogowego. Gdy system początkowo wyświetla okno dialogowe, jego pozycja jest względna względem lewego górnego rogu obszaru klienta okna właściciela. Aplikacja używa wartości zwracanej z DialogBox, aby określić, czy kontynuować operację, czy ją anulować. Poniższe instrukcje definiują procedurę okna dialogowego.

char szItemName[80]; // receives name of item to delete. 
 
BOOL CALLBACK DeleteItemProc(HWND hwndDlg, 
                             UINT message, 
                             WPARAM wParam, 
                             LPARAM lParam) 
{ 
    switch (message) 
    { 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
                    if (!GetDlgItemText(hwndDlg, ID_ITEMNAME, szItemName, 80)) 
                         *szItemName=0; 
 
                    // Fall through. 
 
                case IDCANCEL: 
                    EndDialog(hwndDlg, wParam); 
                    return TRUE; 
            } 
    } 
    return FALSE; 
} 

W tym przykładzie procedura używa GetDlgItemText w celu pobrania bieżącego tekstu z kontrolki edycji zidentyfikowanej przez ID_ITEMNAME. Następnie procedura wywołuje funkcję EndDialog, aby ustawić wartość zwracaną okna dialogowego na IDOK lub IDCANCEL, w zależności od odebranego komunikatu i rozpocząć proces zamykania okna dialogowego. Identyfikatory IDOK i IDCANCEL odpowiadają przyciskom OK i Anuluj. Po wywołaniu procedury EndDialogsystem wysyła dodatkowe komunikaty do procedury w celu zniszczenia okna dialogowego i zwraca wartość zwracaną przez okno dialogowe z powrotem do funkcji, która utworzyła okno dialogowe.

Tworzenie okna dialogowego bez moderowania

Okno dialogowe bez moderowania jest tworzone przy użyciu funkcji CreateDialog, określając identyfikator lub nazwę zasobu szablonu okna dialogowego i wskaźnik do procedury okna dialogowego. createDialog ładuje szablon, tworzy okno dialogowe i opcjonalnie wyświetla go. Aplikacja jest odpowiedzialna za pobieranie i wysyłanie komunikatów wejściowych użytkownika do procedury okna dialogowego.

W poniższym przykładzie aplikacja wyświetla okno dialogowe niemodalne — jeśli jeszcze nie jest wyświetlane — gdy użytkownik kliknie Przejdź do z menu aplikacji. Okno dialogowe zawiera kontrolkę edycji, pole wyboru oraz przyciski OK i Anuluj. Szablon okna dialogowego jest zasobem w pliku wykonywalnym aplikacji i ma identyfikator zasobu DLG_GOTO. Użytkownik wprowadza numer wiersza w kontrolce edycji i sprawdza pole wyboru, aby określić, że numer wiersza jest względny do bieżącego wiersza. Identyfikatory kontrolek są ID_LINE, ID_ABSREL, IDOK i IDCANCEL.

Instrukcje w pierwszej części przykładu tworzą niemodalne okno dialogowe. Te instrukcje w procedurze okna dla głównego okna aplikacji tworzą okno dialogowe, gdy procedura okna odbiera komunikat WM_COMMAND z identyfikatorem menu IDM_GOTO, ale tylko wtedy, gdy zmienna globalna nie zawiera już prawidłowego uchwytu. Drugą częścią przykładu jest główna pętla komunikatów aplikacji. Pętla zawiera funkcję IsDialogMessage, aby upewnić się, że użytkownik może używać klawiaturowego interfejsu okna dialogowego w tym beztrybowym oknie dialogowym. Trzecią częścią przykładu jest procedura okna dialogowego. Procedura pobiera zawartość kontrolki edycji i pole wyboru, gdy użytkownik kliknie przycisk OK. Procedura niszczy okno dialogowe, gdy użytkownik kliknie przycisk Anuluj.

HWND hwndGoto = NULL;  // Window handle of dialog box 
                
...

case WM_COMMAND: 
    switch (LOWORD(wParam)) 
    { 
        case IDM_GOTO: 
            if (!IsWindow(hwndGoto)) 
            { 
                hwndGoto = CreateDialog(hinst, 
                                        MAKEINTRESOURCE(DLG_GOTO), 
                                        hwnd, 
                                        (DLGPROC)GoToProc); 
                ShowWindow(hwndGoto, SW_SHOW); 
            } 
            break; 
    } 
    return 0L; 

W poprzednich instrukcjach CreateDialog jest wywoływana tylko wtedy, gdy hwndGoto nie zawiera prawidłowego uchwytu okna. Dzięki temu aplikacja nie wyświetla jednocześnie dwóch okien dialogowych. Aby obsługiwać tę metodę sprawdzania, procedura okna dialogowego musi być ustawiona na NULL, gdy niszczy okno dialogowe.

Pętla komunikatów dla aplikacji składa się z następujących instrukcji.

BOOL bRet;

while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) 
{ 
    if (bRet == -1)
    {
        // Handle the error and possibly exit
    }
    else if (!IsWindow(hwndGoto) || !IsDialogMessage(hwndGoto, &msg)) 
    { 
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    } 
} 

Pętla sprawdza ważność uchwytu okna dialogowego i wywołuje tylko funkcję IsDialogMessage, jeśli uchwyt jest prawidłowy. IsDialogMessage przetwarza komunikat tylko wtedy, gdy należy do okna dialogowego. W przeciwnym razie zwraca false, a pętla wysyła komunikat do odpowiedniego okna.

Poniższe instrukcje definiują procedurę okna dialogowego.

int iLine;             // Receives line number.
BOOL fRelative;        // Receives check box status. 
 
BOOL CALLBACK GoToProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    BOOL fError; 
 
    switch (message) 
    { 
        case WM_INITDIALOG: 
            CheckDlgButton(hwndDlg, ID_ABSREL, fRelative); 
            return TRUE; 
 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
                    fRelative = IsDlgButtonChecked(hwndDlg, ID_ABSREL); 
                    iLine = GetDlgItemInt(hwndDlg, ID_LINE, &fError, fRelative); 
                    if (fError) 
                    { 
                        MessageBox(hwndDlg, SZINVALIDNUMBER, SZGOTOERR, MB_OK); 
                        SendDlgItemMessage(hwndDlg, ID_LINE, EM_SETSEL, 0, -1L); 
                    } 
                    else 

                    // Notify the owner window to carry out the task. 
 
                    return TRUE; 
 
                case IDCANCEL: 
                    DestroyWindow(hwndDlg); 
                    hwndGoto = NULL; 
                    return TRUE; 
            } 
    } 
    return FALSE; 
} 

W poprzednich instrukcjach procedura przetwarza komunikaty WM_INITDIALOG i WM_COMMAND. Podczas przetwarzania WM_INITDIALOG procedura inicjuje pole wyboru, przekazując bieżącą wartość zmiennej globalnej do CheckDlgButton. Następnie procedura zwraca true, aby skierować system do ustawienia domyślnego fokusu wejściowego.

Podczas przetwarzania WM_COMMAND procedura zamyka okno dialogowe tylko wtedy, gdy użytkownik kliknie przycisk Anuluj — czyli przycisk o identyfikatorze IDCANCEL. Aby zamknąć niesterowane okno dialogowe, procedura musi wywołać DestroyWindow. Zwróć uwagę, że procedura ustawia również zmienną na null, aby upewnić się, że inne instrukcje zależne od tej zmiennej działają poprawnie.

Jeśli użytkownik kliknie przycisk OK, procedura pobiera bieżący stan pola wyboru i przypisuje go do zmiennej fRelative. Następnie używa zmiennej do pobrania numeru wiersza z kontrolki edycji. GetDlgItemInt tłumaczy tekst w kontrolce edycji na liczbę całkowitą. Wartość fRelative określa, czy funkcja interpretuje liczbę jako wartość podpisaną, czy niepodpisaną. Jeśli tekst kontrolki edycji nie jest prawidłową liczbą, GetDlgItemInt ustawia wartość zmiennej fError na niezerową. Procedura sprawdza tę wartość, aby określić, czy wyświetlić komunikat o błędzie, czy wykonać zadanie. W przypadku błędu procedura okna dialogowego wysyła komunikat do kontrolki edycji, kierując go do wybrania tekstu w kontrolce, aby użytkownik mógł łatwo go zamienić. Jeśli GetDlgItemInt nie zwraca błędu, procedura może wykonać żądane zadanie lub wysłać komunikat do okna właściciela, kierując go do przeprowadzenia operacji.

Inicjowanie okna dialogowego

** Inicjujesz okno dialogowe i jego zawartość, inicjując komunikat WM_INITDIALOG. Najczęstszym zadaniem jest zainicjowanie kontrolek w celu odzwierciedlenia bieżących ustawień okna dialogowego. Innym typowym zadaniem jest wyśrodkowanie okna dialogowego na ekranie lub w oknie właściciela. Przydatne zadanie dla niektórych okien dialogowych polega na ustawieniu fokusu wejściowego na określoną kontrolkę, a nie zaakceptowanie domyślnego fokusu wejściowego.

W poniższym przykładzie procedura okna dialogowego koncentruje okno dialogowe i ustawia fokus wejściowy podczas przetwarzania komunikatu WM_INITDIALOG. Aby wyśrodkować okno dialogowe, procedura pobiera prostokąty okien dla okna dialogowego i okna właściciela i oblicza nową pozycję dla okna dialogowego. Aby ustawić fokus wejściowy, procedura sprawdza parametr wParam w celu określenia identyfikatora domyślnego fokusu wejściowego.

HWND hwndOwner; 
RECT rc, rcDlg, rcOwner; 

....
 
case WM_INITDIALOG: 

    // Get the owner window and dialog box rectangles. 

    if ((hwndOwner = GetParent(hwndDlg)) == NULL) 
    {
        hwndOwner = GetDesktopWindow(); 
    }

    GetWindowRect(hwndOwner, &rcOwner); 
    GetWindowRect(hwndDlg, &rcDlg); 
    CopyRect(&rc, &rcOwner); 

    // Offset the owner and dialog box rectangles so that right and bottom 
    // values represent the width and height, and then offset the owner again 
    // to discard space taken up by the dialog box. 

    OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); 
    OffsetRect(&rc, -rc.left, -rc.top); 
    OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); 

    // The new position is the sum of half the remaining space and the owner's 
    // original position. 

    SetWindowPos(hwndDlg, 
                 HWND_TOP, 
                 rcOwner.left + (rc.right / 2), 
                 rcOwner.top + (rc.bottom / 2), 
                 0, 0,          // Ignores size arguments. 
                 SWP_NOSIZE); 

    if (GetDlgCtrlID((HWND) wParam) != ID_ITEMNAME) 
    { 
        SetFocus(GetDlgItem(hwndDlg, ID_ITEMNAME)); 
        return FALSE; 
    } 
    return TRUE; 

W poprzednich instrukcjach procedura używa funkcji GetParent, aby pobrać uchwyt okna właściciela do okna dialogowego. Funkcja zwraca uchwyt okna właściciela do okien dialogowych, a uchwyt okna nadrzędnego do okien podrzędnych. Ponieważ aplikacja może utworzyć okno dialogowe, które nie ma właściciela, procedura sprawdza zwrócony uchwyt i używa funkcji GetDesktopWindow w celu pobrania uchwytu okna pulpitu, jeśli to konieczne. Po obliczeniu nowej pozycji procedura używa funkcji SetWindowPos, aby przenieść okno dialogowe, określając wartość HWND_TOP, aby upewnić się, że okno dialogowe pozostanie w górnej części okna właściciela.

Przed ustawieniem fokusu wejściowego procedura sprawdza identyfikator kontrolki domyślnego fokusu wejściowego. System przekazuje uchwyt okna domyślnego fokusu liniowego w parametrze wParam. Funkcja GetDlgCtrlID zwraca identyfikator kontrolki zidentyfikowany przez uchwyt okna. Jeśli identyfikator nie jest zgodny z prawidłowym identyfikatorem, procedura używa funkcji SetFocus, aby ustawić fokus wejściowy. Funkcja GetDlgItem jest wymagana do uzyskania uchwytu okna dla żądanego elementu sterującego.

Tworzenie szablonu w pamięci

Aplikacje czasami dostosowują lub modyfikują zawartość okien dialogowych w zależności od bieżącego stanu przetwarzanych danych. W takich przypadkach nie jest praktyczne udostępnienie wszystkich możliwych szablonów okien dialogowych jako zasobów w pliku wykonywalny aplikacji. Jednak tworzenie szablonów w pamięci daje aplikacji większą elastyczność dostosowywania się do wszelkich okoliczności.

W poniższym przykładzie aplikacja tworzy szablon w pamięci dla modalnego okna dialogowego zawierającego komunikat i OK i przycisków Pomoc.

W szablonie okna dialogowego wszystkie ciągi znaków, takie jak tytuły okna dialogowego i przycisku, muszą być ciągami Unicode. W tym przykładzie użyto funkcji MultiByteToWideChar w celu wygenerowania tych ciągów Unicode.

Struktury DLGITEMTEMPLATE w szablonie okna dialogowego muszą być wyrównane do granic DWORD. W celu wyrównania tych struktur w tym przykładzie użyto procedury pomocniczej, która przyjmuje wskaźnik wejściowy i zwraca najbliższy wskaźnik wyrównany do granicy DWORD.

#define ID_HELP   150
#define ID_TEXT   200

LPWORD lpwAlign(LPWORD lpIn)
{
    ULONG ul;

    ul = (ULONG)lpIn;
    ul ++;
    ul >>=1;
    ul <<=1;
    return (LPWORD)ul;
}

LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, LPSTR lpszMessage)
{
    HGLOBAL hgbl;
    LPDLGTEMPLATE lpdt;
    LPDLGITEMTEMPLATE lpdit;
    LPWORD lpw;
    LPWSTR lpwsz;
    LRESULT ret;
    int nchar;

    hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
    if (!hgbl)
        return -1;
 
    lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
 
    // Define a dialog box.
 
    lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
    lpdt->cdit = 3;         // Number of controls
    lpdt->x  = 10;  lpdt->y  = 10;
    lpdt->cx = 100; lpdt->cy = 100;

    lpw = (LPWORD)(lpdt + 1);
    *lpw++ = 0;             // No menu
    *lpw++ = 0;             // Predefined dialog box class (by default)

    lpwsz = (LPWSTR)lpw;
    nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "My Dialog", -1, lpwsz, 50);
    lpw += nchar;

    //-----------------------
    // Define an OK button.
    //-----------------------
    lpw = lpwAlign(lpw);    // Align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE)lpw;
    lpdit->x  = 10; lpdit->y  = 70;
    lpdit->cx = 80; lpdit->cy = 20;
    lpdit->id = IDOK;       // OK button identifier
    lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;

    lpw = (LPWORD)(lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0080;        // Button class

    lpwsz = (LPWSTR)lpw;
    nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "OK", -1, lpwsz, 50);
    lpw += nchar;
    *lpw++ = 0;             // No creation data

    //-----------------------
    // Define a Help button.
    //-----------------------
    lpw = lpwAlign(lpw);    // Align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE)lpw;
    lpdit->x  = 55; lpdit->y  = 10;
    lpdit->cx = 40; lpdit->cy = 20;
    lpdit->id = ID_HELP;    // Help button identifier
    lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;

    lpw = (LPWORD)(lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0080;        // Button class atom

    lpwsz = (LPWSTR)lpw;
    nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Help", -1, lpwsz, 50);
    lpw += nchar;
    *lpw++ = 0;             // No creation data

    //-----------------------
    // Define a static text control.
    //-----------------------
    lpw = lpwAlign(lpw);    // Align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE)lpw;
    lpdit->x  = 10; lpdit->y  = 10;
    lpdit->cx = 40; lpdit->cy = 20;
    lpdit->id = ID_TEXT;    // Text identifier
    lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;

    lpw = (LPWORD)(lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0082;        // Static class

    for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*lpszMessage++;);
    lpw = (LPWORD)lpwsz;
    *lpw++ = 0;             // No creation data

    GlobalUnlock(hgbl); 
    ret = DialogBoxIndirect(hinst, 
                           (LPDLGTEMPLATE)hgbl, 
                           hwndOwner, 
                           (DLGPROC)DialogProc); 
    GlobalFree(hgbl); 
    return ret; 
}