2017年5月22日星期一

Win对话框

# 使用对话框 (Windows)


您可以使用对话框来显示信息和从用户输入提示。您的应用程序加载和初始化对话框、 处理用户输入,并破坏对话框中,当用户完成任务。处理对话框的过程各不相同,取决于是否对话框是模式对话框或无模式。一个模态的对话框要求用户激活应用程序中的另一个窗口之前关闭该对话框。然而,用户可以激活 windows 在不同的应用程序。无模式对话框并不需要来自用户的即时响应。它是类似于一个包含控件的主要窗口。

以下各节讨论如何使用这两种类型的对话框。

    显示一个消息框
    创建一个模态的对话框
    创建无模式对话框
    初始化对话框
    在内存中创建一个模板

## 显示一个消息框

模态对话框的最简单形式是消息框。大多数应用程序使用消息框,警告用户的错误,并提示输入有关如何继续后出现了一个错误的方向。创建使用消息框或MessageBoxEx函数,指定消息和数目和类型的按钮以显示一个消息框。系统将创建一个模态对话框,提供其自己的对话框模板和程序。在用户关闭该消息框后, MessageBox或MessageBoxEx返回一个值,标识由用户选择关闭消息框的按钮。

在以下示例中,应用程序将显示一个消息框,提示用户输入行动后发生了错误状况。该消息框显示描述错误条件以及如何解决它的消息。MB_YESNO风格指导MessageBox提供两个按钮,用户可以选择如何继续:
```c
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;
}

```

下图显示了前面的代码示例的输出:

## 创建一个模态的对话框

您通过使用DialogBox函数创建一个模态的对话框。您必须指定的标识符或名称的对话框模板资源和一个指针,指向对话框过程。DialogBox函数加载模板,显示对话框,并处理所有用户输入,直到用户关闭对话框。

在以下示例中,应用程序显示一个模态对话框,当用户从应用程序菜单中单击删除项目。该对话框包含编辑控件 (用户在其中输入项目的名称) 和确定和取消按钮。这些控件的控件标识符分别为 ID_ITEMNAME、 IDOK 和 IDCANCEL。

该示例的第一部分由创建模态对话框的语句组成。这些语句,应用程序的主窗口的窗口过程中创建该对话框时,系统接收有 IDM_DELETEITEM 菜单标识符的WM_COMMAND消息。该示例的第二部分是对话框过程,其中检索编辑控件的内容并关闭对话框后接收WM_COMMAND消息。

下面的语句创建模态对话框。对话框模板是应用程序的可执行文件中的资源,具有 DLG_DELETEITEM 的资源标识符。
```c
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;

```
在此示例中,应用程序指定其主窗口为对话框的所有者窗口。当系统最初将显示对话框中时,它的位置是相对于所有者窗口客户区的左上角。应用程序使用从DialogBox返回的值来确定是否继续操作或取消它。下面的语句定义的对话框过程。
```c
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;
}
```

在此示例中,程序使用GetDlgItemText从由 ID_ITEMNAME 标识的编辑控件检索当前文本。该过程然后调用EndDialog函数来设置对话框的返回值为 IDOK 或 IDCANCEL,根据收到的消息,并开始关闭对话框的过程。IDOK 和 IDCANCEL 标识符对应的确定和取消按钮。该过程调用EndDialog后,系统将额外的消息发送到摧毁对话框中的程序,并将对话框的返回值返回给创建对话框的功能。
## 创建无模式对话框

通过使用CreateDialog函数,指定的标识符或名称的对话框模板资源和一个指针,指向对话框过程创建无模式对话框。CreateDialog加载模板,创建对话框,并选择显示它。您的应用程序负责检索和调度用户对对话框过程的输入的消息。

在以下示例中,该应用程序显示一个非模态的对话框 — — 如果还不显示它,则 — — 当用户单击转到从一个应用程序菜单。该对话框包含编辑控件、 复选框和确定和取消按钮。对话框模板是应用程序的可执行文件中的资源,具有 DLG_GOTO 的资源标识符。用户在编辑控件中输入行号,并选中该复选框,以指定的行号是相对于当前行。控制标识符是 ID_LINE、 ID_ABSREL、 IDOK 和 IDCANCEL。

在该示例的第一部分中的语句创建无模式对话框。这些语句,应用程序的主窗口的窗口过程中创建对话框,当窗口过程接收WM_COMMAND消息有 IDM_GOTO 菜单标识符,但只有当全局变量已经不包含有效的句柄。该示例的第二部分是应用程序的主消息循环。回路包括IsDialogMessage功能,以确保用户可以在此无模式对话框使用对话框键盘界面。示例的第三部分是对话框过程。当用户单击确定按钮,程序检索编辑控件和复选框的内容。当用户单击取消按钮时,程序销毁对话框。
```c
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;
```

在上面的语句, CreateDialog被称为只当hwndGoto不包含有效的窗口的句柄。这可以确保应用程序不在同一时间显示两个对话框。为了支持这种检查方法,对话框过程必须设置为NULL时它将销毁对话框。

为应用程序的消息循环由以下语句组成。
```c
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;
}
```

在上面的语句,过程处理WM_INITDIALOG和WM_COMMAND消息。在WM_INITDIALOG处理,程序通过将全局变量的当前值传递到CheckDlgButton初始化复选框。程序然后返回TRUE以指示系统设置默认输入的焦点。

WM_COMMAND加工过程中的程序关闭对话框仅当用户单击取消按钮 — — 那就是,有 IDCANCEL 标识符的按钮。程序必须调用窗口关闭无模式对话框。请注意,该过程也将变量设置为NULL ,确保其他取决于此变量的语句正确操作。

如果用户单击确定按钮,程序检索复选框的当前状态,并将它赋给fRelative变量。然后使用该变量从编辑控件中检索的行号。 GetDlgItemInt编辑控件中的文本转换为一个整数。FRelative的值确定是否功能解释数字作为一个符号或无符号的值。如果编辑控件文本不是有效数字, GetDlgItemInt将能力变量的值设置为非零值。该过程将检查此值来确定是否显示一条错误消息或执行这项任务。在出现错误时,对话框过程将消息发送到编辑控制,指挥它来选择控件中的文本,以便用户可以轻松地替换它。如果GetDlgItemInt不返回一个错误,程序可以执行所请求的任务本身或向所有者窗口,引导它来进行操作的窗口发送一条消息。
## 初始化对话框

你处理WM_INITDIALOG消息时初始化该对话框中,它的内容。最常见的任务是初始化控件,以反映当前对话框中的设置。另一个常见任务是中心一个对话框在屏幕上或在其所有者窗口内。一个有用的任务,为某些对话框是输入的焦点设置到指定的控件,而不是接受默认输入的焦点。

在以下示例中,对话框过程中心对话框中和处理WM_INITDIALOG消息时设置输入的焦点。中心对话框中,该过程检索对话框的所有者窗口的窗口矩形并将计算为对话框中的新位置。若要设置输入的焦点,该过程将检查wParam参数来确定默认输入焦点的标识符。
```c
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;
```

在上面的语句,过程使用GetParent函数来检索对话框的所有者窗口句柄。该函数返回的所有者窗口句柄到对话框和子窗口的父窗口句柄。因为应用程序可以创建一个对话框,有没有所有者,过程检查返回的句柄,使用句柄函数检索桌面窗口句柄,如有必要。在计算后的新位置,程序使用SetWindowPos函数移动对话框,指定 HWND_TOP 值,以确保对话框的所有者窗口的顶部。

在设置之前输入的焦点,该过程将检查默认输入焦点的控件标识符。系统以wParam参数传递默认输入焦点的窗口句柄。 GetDlgCtrlID函数返回由窗口句柄标识的控件的标识符。如果标识符与正确的标识符不匹配,该过程使用SetFocus函数设置输入的焦点。 GetDlgItem函数需要检索所需控件的窗口句柄。
## 在内存中创建一个模板

应用程序有时适应或修改内容的对话框取决于正在处理的数据的当前状态。在这种情况下,它不是实际提供所有可能的对话框模板作为应用程序的可执行文件中的资源。但在内存中创建的模板使应用程序更多的灵活性,以适应任何情况下。

在以下示例中,应用程序创建一个模板在内存中为模态的对话框,其中包含一个消息和确定和帮助按钮。

在对话框模板中,所有字符串的字符集,如对话框框和按钮的标题,必须都是 Unicode 字符串。本示例使用MultiByteToWideChar函数来生成这些 Unicode 字符串。

在对话框模板中的DLGITEMTEMPLATE结构必须在DWORD边界上对齐。若要对齐这些结构,此示例使用 helper 例程将输入的指针并返回最接近的指针对齐DWORD边界上。
```c
#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;
}
```

没有评论:

发表评论