Menggunakan Kotak Dialog

Anda menggunakan kotak dialog untuk menampilkan informasi dan meminta input dari pengguna. Aplikasi Anda memuat dan menginisialisasi kotak dialog, memproses input pengguna, dan menghancurkan kotak dialog saat pengguna menyelesaikan tugas. Proses untuk menangani kotak dialog bervariasi, tergantung pada apakah kotak dialog modal atau tanpa mode. Kotak dialog modal mengharuskan pengguna menutup kotak dialog sebelum mengaktifkan jendela lain dalam aplikasi. Namun, pengguna dapat mengaktifkan jendela di aplikasi yang berbeda. Kotak dialog tanpa mode tidak memerlukan respons langsung dari pengguna. Ini mirip dengan jendela utama yang berisi kontrol.

Bagian berikut membahas cara menggunakan kedua jenis kotak dialog.

Menampilkan Kotak Pesan

Bentuk paling sederhana dari kotak dialog modal adalah kotak pesan. Sebagian besar aplikasi menggunakan kotak pesan untuk memperingatkan pengguna tentang kesalahan dan meminta petunjuk tentang cara melanjutkan setelah kesalahan terjadi. Anda membuat kotak pesan dengan menggunakan fungsi MessageBox atau MessageBoxEx , menentukan pesan dan nomor dan jenis tombol untuk ditampilkan. Sistem membuat kotak dialog modal, menyediakan templat dan prosedur kotak dialognya sendiri. Setelah pengguna menutup kotak pesan, MessageBox atau MessageBoxEx mengembalikan nilai yang mengidentifikasi tombol yang dipilih oleh pengguna untuk menutup kotak pesan.

Dalam contoh berikut, aplikasi menampilkan kotak pesan yang meminta tindakan kepada pengguna setelah kondisi kesalahan terjadi. Kotak pesan menampilkan pesan yang menjelaskan kondisi kesalahan dan cara mengatasinya. Gaya MB_YESNO mengarahkan MessageBox untuk menyediakan dua tombol yang dapat dipilih pengguna untuk melanjutkan:

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

Gambar berikut menunjukkan output dari contoh kode sebelumnya:

kotak pesan

Membuat Kotak Dialog Modal

Anda membuat kotak dialog modal dengan menggunakan fungsi Kotak Dialog . Anda harus menentukan pengidentifikasi atau nama sumber daya templat kotak dialog dan penunjuk ke prosedur kotak dialog. Fungsi DialogBox memuat templat, menampilkan kotak dialog, dan memproses semua input pengguna hingga pengguna menutup kotak dialog.

Dalam contoh berikut, aplikasi menampilkan kotak dialog modal saat pengguna mengklik Hapus Item dari menu aplikasi. Kotak dialog berisi kontrol edit (di mana pengguna memasukkan nama item) dan tombol OK dan Batal . Pengidentifikasi kontrol untuk kontrol ini masing-masing ID_ITEMNAME, IDOK, dan IDCANCEL.

Bagian pertama dari contoh terdiri dari pernyataan yang membuat kotak dialog modal. Pernyataan-pernyataan ini, dalam prosedur jendela untuk jendela utama aplikasi, buat kotak dialog saat sistem menerima pesan WM_COMMAND yang memiliki pengidentifikasi menu IDM_DELETEITEM. Bagian kedua dari contoh adalah prosedur kotak dialog, yang mengambil konten kontrol edit dan menutup kotak dialog saat menerima pesan WM_COMMAND .

Pernyataan berikut membuat kotak dialog modal. Templat kotak dialog adalah sumber daya dalam file yang dapat dieksekusi aplikasi dan memiliki pengidentifikasi sumber daya 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; 

Dalam contoh ini, aplikasi menentukan jendela utamanya sebagai jendela pemilik untuk kotak dialog. Ketika sistem awalnya menampilkan kotak dialog, posisinya relatif terhadap sudut kiri atas area klien jendela pemilik. Aplikasi menggunakan nilai pengembalian dari DialogBox untuk menentukan apakah akan melanjutkan operasi atau membatalkannya. Pernyataan berikut menentukan prosedur kotak dialog.

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

Dalam contoh ini, prosedur menggunakan GetDlgItemText untuk mengambil teks saat ini dari kontrol edit yang diidentifikasi oleh ID_ITEMNAME. Prosedur kemudian memanggil fungsi EndDialog untuk mengatur nilai pengembalian kotak dialog ke IDOK atau IDCANCEL, tergantung pada pesan yang diterima, dan untuk memulai proses penutupan kotak dialog. Pengidentifikasi IDOK dan IDCANCEL sesuai dengan tombol OK dan Batal . Setelah prosedur memanggil EndDialog, sistem mengirim pesan tambahan ke prosedur untuk menghancurkan kotak dialog dan mengembalikan nilai pengembalian kotak dialog ke fungsi yang membuat kotak dialog.

Membuat Kotak Dialog Tanpa Mode

Anda membuat kotak dialog tanpa mode dengan menggunakan fungsi CreateDialog , menentukan pengidentifikasi atau nama sumber daya templat kotak dialog dan penunjuk ke prosedur kotak dialog. CreateDialog memuat templat, membuat kotak dialog, dan secara opsional menampilkannya. Aplikasi Anda bertanggung jawab untuk mengambil dan mengirimkan pesan input pengguna ke prosedur kotak dialog.

Dalam contoh berikut, aplikasi menampilkan kotak dialog tanpa mode — jika belum ditampilkan — saat pengguna mengklik Buka dari menu aplikasi. Kotak dialog berisi kontrol edit, kotak centang, dan tombol OK dan Batal . Templat kotak dialog adalah sumber daya dalam file yang dapat dieksekusi aplikasi dan memiliki pengidentifikasi sumber daya DLG_GOTO. Pengguna memasukkan nomor baris dalam kontrol edit dan mencentang kotak centang untuk menentukan bahwa nomor baris relatif terhadap baris saat ini. Pengidentifikasi kontrol ID_LINE, ID_ABSREL, IDOK, dan IDCANCEL.

Pernyataan di bagian pertama dari contoh membuat kotak dialog tanpa mode. Pernyataan-pernyataan ini, dalam prosedur jendela untuk jendela utama aplikasi, buat kotak dialog ketika prosedur jendela menerima pesan WM_COMMAND memiliki pengidentifikasi menu IDM_GOTO, tetapi hanya jika variabel global belum berisi handel yang valid. Bagian kedua dari contoh adalah perulangan pesan utama aplikasi. Perulangan menyertakan fungsi IsDialogMessage untuk memastikan bahwa pengguna dapat menggunakan antarmuka keyboard kotak dialog dalam kotak dialog tanpa mode ini. Bagian ketiga dari contoh adalah prosedur kotak dialog. Prosedur ini mengambil konten kontrol edit dan kotak centang saat pengguna mengklik tombol OK . Prosedur ini menghancurkan kotak dialog saat pengguna mengklik tombol Batalkan .

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; 

Dalam pernyataan sebelumnya, CreateDialog hanya dipanggil jika hwndGoto tidak berisi handel jendela yang valid. Ini memastikan bahwa aplikasi tidak menampilkan dua kotak dialog secara bersamaan. Untuk mendukung metode pemeriksaan ini, prosedur dialog harus diatur ke NULL ketika menghancurkan kotak dialog.

Perulangan pesan untuk aplikasi terdiri dari pernyataan berikut.

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

Perulangan memeriksa validitas handel jendela ke kotak dialog dan hanya memanggil fungsi IsDialogMessage jika handel valid. IsDialogMessage hanya memproses pesan jika itu milik kotak dialog. Jika tidak, ia mengembalikan FALSE dan perulangan mengirimkan pesan ke jendela yang sesuai.

Pernyataan berikut menentukan prosedur kotak dialog.

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

Dalam pernyataan sebelumnya, prosedur memproses pesan WM_INITDIALOG dan WM_COMMAND . Selama pemrosesan WM_INITDIALOG , prosedur menginisialisasi kotak centang dengan meneruskan nilai variabel global saat ini ke CheckDlgButton. Prosedur kemudian mengembalikan TRUE untuk mengarahkan sistem untuk mengatur fokus input default.

Selama pemrosesan WM_COMMAND , prosedur menutup kotak dialog hanya jika pengguna mengklik tombol Batal — yaitu, tombol yang memiliki pengidentifikasi IDCANCEL. Prosedur harus memanggil DestroyWindow untuk menutup kotak dialog tanpa mode. Perhatikan bahwa prosedur ini juga mengatur variabel ke NULL untuk memastikan bahwa pernyataan lain yang bergantung pada variabel ini beroperasi dengan benar.

Jika pengguna mengklik tombol OK , prosedur mengambil status kotak centang saat ini dan menetapkannya ke variabel fRelative . Kemudian menggunakan variabel untuk mengambil nomor baris dari kontrol edit. GetDlgItemInt menerjemahkan teks dalam kontrol edit menjadi bilangan bulat. Nilai fRelative menentukan apakah fungsi menginterpretasikan angka sebagai nilai yang ditandatangani atau tidak ditandatangani. Jika teks kontrol edit bukan angka yang valid, GetDlgItemInt mengatur nilai variabel fError ke bukan nol. Prosedur memeriksa nilai ini untuk menentukan apakah akan menampilkan pesan kesalahan atau melakukan tugas. Jika terjadi kesalahan, prosedur kotak dialog mengirim pesan ke kontrol edit, mengarahkannya untuk memilih teks dalam kontrol sehingga pengguna dapat dengan mudah menggantinya. Jika GetDlgItemInt tidak mengembalikan kesalahan, prosedur dapat melakukan tugas yang diminta itu sendiri atau mengirim pesan ke jendela pemilik, mengarahkannya untuk melakukan operasi.

Menginisialisasi Kotak Dialog

Anda menginisialisasi kotak dialog dan isinya saat memproses pesan WM_INITDIALOG . Tugas yang paling umum adalah menginisialisasi kontrol untuk mencerminkan pengaturan kotak dialog saat ini. Tugas umum lainnya adalah mempusat kotak dialog di layar atau di dalam jendela pemiliknya. Tugas yang berguna untuk beberapa kotak dialog adalah mengatur fokus input ke kontrol tertentu daripada menerima fokus input default.

Dalam contoh berikut, prosedur kotak dialog memusatkan kotak dialog dan mengatur fokus input saat memproses pesan WM_INITDIALOG . Untuk memosisikan kotak dialog, prosedur mengambil persegi panjang jendela untuk kotak dialog dan jendela pemilik dan menghitung posisi baru untuk kotak dialog. Untuk mengatur fokus input, prosedur memeriksa parameter wParam untuk menentukan pengidentifikasi fokus input default.

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; 

Dalam pernyataan sebelumnya, prosedur menggunakan fungsi GetParent untuk mengambil handel jendela pemilik ke kotak dialog. Fungsi mengembalikan handel jendela pemilik ke kotak dialog, dan handel jendela induk ke jendela anak. Karena aplikasi dapat membuat kotak dialog yang tidak memiliki pemilik, prosedur memeriksa handel yang dikembalikan dan menggunakan fungsi GetDesktopWindow untuk mengambil handel jendela desktop, jika perlu. Setelah menghitung posisi baru, prosedur menggunakan fungsi SetWindowPos untuk memindahkan kotak dialog, menentukan nilai HWND_TOP untuk memastikan bahwa kotak dialog tetap berada di atas jendela pemilik.

Sebelum mengatur fokus input, prosedur memeriksa pengidentifikasi kontrol fokus input default. Sistem melewati handel jendela fokus input default dalam parameter wParam . Fungsi GetDlgCtrlID mengembalikan pengidentifikasi untuk kontrol yang diidentifikasi oleh handel jendela. Jika pengidentifikasi tidak cocok dengan pengidentifikasi yang benar, prosedur menggunakan fungsi SetFocus untuk mengatur fokus input. Fungsi GetDlgItem diperlukan untuk mengambil handel jendela dari kontrol yang diinginkan.

Membuat Templat dalam Memori

Aplikasi terkadang mengadaptasi atau mengubah konten kotak dialog tergantung pada status data saat ini yang sedang diproses. Dalam kasus seperti itu, tidak praktis untuk menyediakan semua templat kotak dialog yang mungkin sebagai sumber daya dalam file yang dapat dieksekusi aplikasi. Tetapi membuat templat dalam memori memberi aplikasi lebih banyak fleksibilitas untuk beradaptasi dengan keadaan apa pun.

Dalam contoh berikut, aplikasi membuat templat dalam memori untuk kotak dialog modal yang berisi pesan dan tombol OK dan Bantuan .

Dalam templat dialog, semua string karakter, seperti kotak dialog dan judul tombol, harus berupa string Unicode. Contoh ini menggunakan fungsi MultiByteToWideChar untuk menghasilkan string Unicode ini.

Struktur DLGITEMTEMPLATE dalam templat dialog harus diselaraskan pada batas DWORD . Untuk menyelaraskan struktur ini, contoh ini menggunakan rutinitas pembantu yang mengambil penunjuk input dan mengembalikan penunjuk terdekat yang selaras pada batas 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; 
}