Verwenden von Dialogfeldern

Sie verwenden Dialogfelder, um Informationen anzuzeigen und vom Benutzer zur Eingabe aufzufordern. Ihre Anwendung lädt und initialisiert das Dialogfeld, verarbeitet Benutzereingaben und zerstört das Dialogfeld, wenn der Benutzer die Aufgabe abgeschlossen hat. Der Prozess für die Verarbeitung von Dialogfeldern variiert je nachdem, ob das Dialogfeld modal oder moduslos ist. Für ein modales Dialogfeld muss der Benutzer das Dialogfeld schließen, bevor er ein weiteres Fenster in der Anwendung aktiviert. Der Benutzer kann jedoch Fenster in verschiedenen Anwendungen aktivieren. Für ein dialogfeld ohne Modus ist keine sofortige Antwort des Benutzers erforderlich. Es ähnelt einem Standard Fenster, das Steuerelemente enthält.

In den folgenden Abschnitten wird erläutert, wie beide Arten von Dialogfeldern verwendet werden.

Anzeigen eines Meldungsfelds

Die einfachste Form des modalen Dialogfelds ist das Meldungsfeld. Die meisten Anwendungen verwenden Meldungsfelder, um den Benutzer vor Fehlern zu warnen und um nach einem Fehler anweisungen zum weiteren Vorgehen aufzufordern. Sie erstellen ein Meldungsfeld, indem Sie die MessageBox - oder MessageBoxEx-Funktion verwenden und die Nachricht sowie die Anzahl und den Typ der anzuzeigenden Schaltflächen angeben. Das System erstellt ein modales Dialogfeld, das eine eigene Dialogfeldvorlage und eine eigene Prozedur bereitstellt. Nachdem der Benutzer das Meldungsfeld geschlossen hat, gibt MessageBox oder MessageBoxEx einen Wert zurück, der die vom Benutzer zum Schließen des Meldungsfelds ausgewählte Schaltfläche identifiziert.

Im folgenden Beispiel zeigt die Anwendung ein Meldungsfeld an, das den Benutzer zur Eingabe einer Aktion auffordert, nachdem eine Fehlerbedingung aufgetreten ist. Im Meldungsfeld wird die Meldung angezeigt, die die Fehlerbedingung und deren Behebung beschreibt. Der MB_YESNO-Stil weist MessageBox an, zwei Schaltflächen bereitzustellen, mit denen der Benutzer die Vorgehensweise auswählen kann:

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;    
}

Die folgende Abbildung zeigt die Ausgabe aus dem vorherigen Codebeispiel:

Meldungsfeld

Erstellen eines modale Dialogfelds

Sie erstellen ein modales Dialogfeld mithilfe der DialogBox-Funktion . Sie müssen den Bezeichner oder Namen einer Dialogfeldvorlagenressource und einen Zeiger auf die Dialogfeldprozedur angeben. Die DialogBox-Funktion lädt die Vorlage, zeigt das Dialogfeld an und verarbeitet alle Benutzereingaben, bis der Benutzer das Dialogfeld schließt.

Im folgenden Beispiel zeigt die Anwendung ein modales Dialogfeld an, wenn der Benutzer aus einem Anwendungsmenü auf Element löschen klickt. Das Dialogfeld enthält ein Bearbeitungssteuerelement (in dem der Benutzer den Namen eines Elements eingibt) sowie die Schaltflächen OK und Abbrechen . Die Steuerelementbezeichner für diese Steuerelemente sind ID_ITEMNAME, IDOK bzw. IDCANCEL.

Der erste Teil des Beispiels besteht aus den Anweisungen, die das modale Dialogfeld erstellen. Diese Anweisungen erstellen in der Fensterprozedur für das Standard Fenster der Anwendung das Dialogfeld, wenn das System eine WM_COMMAND Nachricht mit dem IDM_DELETEITEM Menübezeichner empfängt. Der zweite Teil des Beispiels ist die Dialogfeldprozedur, die den Inhalt des Bearbeitungssteuerelements abruft und das Dialogfeld beim Empfang einer WM_COMMAND-Nachricht schließt.

Mit den folgenden Anweisungen wird das modale Dialogfeld erstellt. Die Dialogfeldvorlage ist eine Ressource in der ausführbaren Datei der Anwendung und verfügt über den Ressourcenbezeichner 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; 

In diesem Beispiel gibt die Anwendung ihr Standard Fenster als Besitzerfenster für das Dialogfeld an. Wenn das System das Dialogfeld zunächst anzeigt, ist seine Position relativ zur oberen linken Ecke des Clientbereichs des Besitzerfensters. Die Anwendung verwendet den Rückgabewert aus DialogBox , um zu bestimmen, ob der Vorgang fortgesetzt oder abgebrochen werden soll. Die folgenden Anweisungen definieren die Dialogfeldprozedur.

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; 
} 

In diesem Beispiel verwendet die Prozedur GetDlgItemText , um den aktuellen Text aus dem von ID_ITEMNAME identifizierten Bearbeitungssteuerelement abzurufen. Die Prozedur ruft dann die EndDialog-Funktion auf, um den Rückgabewert des Dialogfelds abhängig von der empfangenen Nachricht entweder auf IDOK oder IDCANCEL festzulegen, und um mit dem Schließen des Dialogfelds zu beginnen. Die IDOK- und IDCANCEL-Bezeichner entsprechen den Schaltflächen OK und Abbrechen . Nachdem die Prozedur EndDialog aufgerufen hat, sendet das System zusätzliche Nachrichten an die Prozedur, um das Dialogfeld zu zerstören, und gibt den Rückgabewert des Dialogfelds an die Funktion zurück, die das Dialogfeld erstellt hat.

Erstellen eines dialogfelds ohne Modus

Sie erstellen ein dialogfeld ohne Modus, indem Sie die CreateDialog-Funktion verwenden, indem Sie den Bezeichner oder Namen einer Dialogfeldvorlagenressource und einen Zeiger auf die Dialogfeldprozedur angeben. CreateDialog lädt die Vorlage, erstellt das Dialogfeld und zeigt es optional an. Ihre Anwendung ist für das Abrufen und Senden von Benutzereingabenachrichten an die Dialogfeldprozedur verantwortlich.

Im folgenden Beispiel zeigt die Anwendung ein dialogfeld ohne Modus an , wenn es nicht bereits angezeigt wird, wenn der Benutzer in einem Anwendungsmenü auf Gehe zu klickt. Das Dialogfeld enthält ein Bearbeitungssteuerelement, ein Kontrollkästchen sowie die Schaltflächen OK und Abbrechen . Die Dialogfeldvorlage ist eine Ressource in der ausführbaren Datei der Anwendung und verfügt über den Ressourcenbezeichner DLG_GOTO. Der Benutzer gibt eine Zeilennummer in das Bearbeitungssteuerelement ein und aktiviert das Kontrollkästchen, um anzugeben, dass die Zeilennummer relativ zur aktuellen Zeile ist. Die Steuerelementbezeichner sind ID_LINE, ID_ABSREL, IDOK und IDCANCEL.

Die Anweisungen im ersten Teil des Beispiels erstellen das dialogfeld ohne Modus. Diese Anweisungen erstellen in der Fensterprozedur für das Standard Fenster der Anwendung das Dialogfeld, wenn die Fensterprozedur eine WM_COMMAND Nachricht mit dem IDM_GOTO Menübezeichner empfängt, aber nur, wenn die globale Variable noch kein gültiges Handle enthält. Der zweite Teil des Beispiels ist die Standard Nachrichtenschleife der Anwendung. Die Schleife enthält die IsDialogMessage-Funktion , um sicherzustellen, dass der Benutzer die Dialogfeldtastaturschnittstelle in diesem moduslosen Dialogfeld verwenden kann. Der dritte Teil des Beispiels ist die Dialogfeldprozedur. Die Prozedur ruft den Inhalt des Bearbeitungssteuerelements und des Kontrollkästchens ab, wenn der Benutzer auf die Schaltfläche OK klickt. Die Prozedur zerstört das Dialogfeld, wenn der Benutzer auf die Schaltfläche Abbrechen klickt.

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; 

In den vorherigen Anweisungen wird CreateDialog nur aufgerufen, wenn hwndGoto kein gültiges Fensterhandle enthalten ist. Dadurch wird sichergestellt, dass die Anwendung nicht zwei Dialogfelder gleichzeitig anzeigt. Um diese Methode der Überprüfung zu unterstützen, muss die Dialogprozedur auf NULL festgelegt werden, wenn sie das Dialogfeld zerstört.

Die Nachrichtenschleife für eine Anwendung besteht aus den folgenden Anweisungen.

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); 
    } 
} 

Die Schleife überprüft die Gültigkeit des Fensterhandles für das Dialogfeld und ruft die IsDialogMessage-Funktion nur auf, wenn das Handle gültig ist. IsDialogMessage verarbeitet die Nachricht nur, wenn sie zum Dialogfeld gehört. Andernfalls wird FALSE zurückgegeben, und die Schleife sendet die Nachricht an das entsprechende Fenster.

Die folgenden Anweisungen definieren die Dialogfeldprozedur.

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; 
} 

In den vorherigen Anweisungen verarbeitet die Prozedur die WM_INITDIALOG und WM_COMMAND Nachrichten. Während WM_INITDIALOG Verarbeitung initialisiert die Prozedur das Kontrollkästchen, indem der aktuelle Wert der globalen Variablen an CheckDlgButton übergeben wird. Die Prozedur gibt dann TRUE zurück, um das System anweisen, den Standardeingabefokus festzulegen.

Während WM_COMMAND Verarbeitung schließt die Prozedur das Dialogfeld nur, wenn der Benutzer auf die Schaltfläche Abbrechen klickt, d. h. die Schaltfläche mit dem IDCANCEL-Bezeichner. Die Prozedur muss DestroyWindow aufrufen, um ein dialogfeld ohne Modus zu schließen. Beachten Sie, dass die Prozedur auch die Variable auf NULL festlegt, um sicherzustellen, dass andere Anweisungen, die von dieser Variablen abhängen, ordnungsgemäß ausgeführt werden.

Wenn der Benutzer auf die Schaltfläche OK klickt, ruft die Prozedur den aktuellen Zustand des Kontrollkästchens ab und weist es der Variablen fRelative zu. Anschließend wird die Variable verwendet, um die Zeilennummer aus dem Bearbeitungssteuerelement abzurufen. GetDlgItemInt übersetzt den Text im Bearbeitungssteuerelement in eine ganze Zahl. Der Wert von fRelative bestimmt, ob die Funktion die Zahl als vorzeichenierten oder unsignierten Wert interpretiert. Wenn der Bearbeitungssteuerelementtext keine gültige Zahl ist, legt GetDlgItemInt den Wert der fError-Variablen auf nonzero fest. Die Prozedur überprüft diesen Wert, um zu ermitteln, ob eine Fehlermeldung angezeigt oder die Aufgabe ausgeführt werden soll. Im Falle eines Fehlers sendet die Dialogfeldprozedur eine Nachricht an das Bearbeitungssteuerelement und weist es an, den Text im Steuerelement auszuwählen, sodass der Benutzer ihn problemlos ersetzen kann. Wenn GetDlgItemInt keinen Fehler zurückgibt, kann die Prozedur entweder die angeforderte Aufgabe selbst ausführen oder eine Nachricht an das Besitzerfenster senden und ihn anweisen, den Vorgang auszuführen.

Initialisieren eines Dialogfelds

Sie initialisieren das Dialogfeld und seinen Inhalt, während Sie die WM_INITDIALOG Nachricht verarbeiten. Die häufigste Aufgabe besteht darin, die Steuerelemente zu initialisieren, um die aktuellen Dialogfeldeinstellungen widerzuspiegeln. Eine weitere häufige Aufgabe besteht darin, ein Dialogfeld auf dem Bildschirm oder innerhalb des Besitzerfensters zu zentrieren. Eine nützliche Aufgabe für einige Dialogfelder besteht darin, den Eingabefokus auf ein angegebenes Steuerelement festzulegen, anstatt den Standardeingabefokus zu akzeptieren.

Im folgenden Beispiel zentriert die Dialogfeldprozedur das Dialogfeld und legt den Eingabefokus fest, während die WM_INITDIALOG Nachricht verarbeitet wird. Um das Dialogfeld zu zentrieren, ruft die Prozedur die Fensterrechtecke für das Dialogfeld und das Besitzerfenster ab und berechnet eine neue Position für das Dialogfeld. Um den Eingabefokus festzulegen, überprüft die Prozedur den wParam-Parameter , um den Bezeichner des Standardeingabefokus zu bestimmen.

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; 

In den vorherigen Anweisungen verwendet die Prozedur die GetParent-Funktion , um das Besitzerfensterhandle in ein Dialogfeld abzurufen. Die Funktion gibt das Handle des Besitzerfensters an Dialogfelder und das Handle des übergeordneten Fensters an untergeordnete Fenster zurück. Da eine Anwendung ein Dialogfeld ohne Besitzer erstellen kann, überprüft die Prozedur das zurückgegebene Handle und verwendet bei Bedarf die GetDesktopWindow-Funktion , um das Desktopfensterhandle abzurufen. Nach dem Berechnen der neuen Position verwendet die Prozedur die SetWindowPos-Funktion , um das Dialogfeld zu verschieben. Dabei wird der wert HWND_TOP angegeben, um sicherzustellen, dass das Dialogfeld über dem Besitzerfenster verbleibt.

Vor dem Festlegen des Eingabefokus überprüft die Prozedur den Steuerelementbezeichner des Standardeingabefokus. Das System übergibt das Fensterhandle des Standardeingabefokus im wParam-Parameter . Die GetDlgCtrlID-Funktion gibt den Bezeichner für das Steuerelement zurück, das durch das Fensterhandle identifiziert wird. Wenn der Bezeichner nicht mit dem richtigen Bezeichner übereinstimmt, verwendet die Prozedur die SetFocus-Funktion , um den Eingabefokus festzulegen. Die GetDlgItem-Funktion ist erforderlich, um das Fensterhandle des gewünschten Steuerelements abzurufen.

Erstellen einer Vorlage im Arbeitsspeicher

Anwendungen passen manchmal den Inhalt von Dialogfeldern je nach aktuellem Status der verarbeiteten Daten an oder ändern sie. In solchen Fällen ist es nicht sinnvoll, alle möglichen Dialogfeldvorlagen als Ressourcen in der ausführbaren Datei der Anwendung bereitzustellen. Das Erstellen von Vorlagen im Arbeitsspeicher bietet der Anwendung jedoch mehr Flexibilität, sich an alle Umstände anzupassen.

Im folgenden Beispiel erstellt die Anwendung eine Vorlage im Arbeitsspeicher für ein modales Dialogfeld, das eine Nachricht und die Schaltflächen OK und Hilfe enthält.

In einer Dialogvorlage müssen alle Zeichenfolgen, z. B. das Dialogfeld und die Schaltflächentitel, Unicode-Zeichenfolgen sein. In diesem Beispiel wird die MultiByteToWideChar-Funktion verwendet, um diese Unicode-Zeichenfolgen zu generieren.

Die DLGITEMTEMPLATE-Strukturen in einer Dialogvorlage müssen an DWORD-Grenzen ausgerichtet sein. Zum Ausrichten dieser Strukturen wird in diesem Beispiel eine Hilfsroutine verwendet, die einen Eingabezeiger akzeptiert und den nächstgelegenen Zeiger zurückgibt, der an einer DWORD-Grenze ausgerichtet ist.

#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; 
}