对话框编程注意事项

本概述讨论了有关对话框的一些编程注意事项。

概述包括以下主题。

对话框过程

对话框过程类似于窗口过程,当过程具有要提供的信息或要执行的任务时,系统会向过程发送消息。与窗口过程不同,对话框过程从不调用 DefWindowProc 函数。 相反,如果处理消息,则返回 TRUE ;否则返回 FALSE

每个对话框过程具有以下形式:

BOOL CALLBACK DlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    switch (message) 
    { 
 
        // Place message cases here. 
 
        default: 
            return FALSE; 
    } 
}

过程参数的作用与在窗口过程中相同, hwndDlg 参数接收对话框的窗口句柄。

大多数对话框过程处理控件发送 的WM_INITDIALOG 消息和 WM_COMMAND 消息,但如果处理任何其他消息,则很少处理。 如果对话框过程不处理消息,则必须返回 FALSE 以指示系统在内部处理消息。 此规则的唯一例外是 WM_INITDIALOG 消息。 对话框过程必须返回 TRUE ,以指示系统进一步处理 WM_INITDIALOG 消息。 在任何情况下,该过程不得调用 DefWindowProc

WM_INITDIALOG消息

系统不会向对话框过程发送 WM_CREATE 消息。 相反,它会在创建对话框及其所有控件时发送 WM_INITDIALOG 消息,但在显示对话框之前。 该过程应执行所需的任何初始化,以确保对话框显示与任务关联的当前设置。 例如,当对话框包含显示当前驱动器和目录的控件时,该过程必须确定当前驱动器和目录,并将控件设置为该值。

该过程可以使用 SetDlgItemTextCheckDlgButton 等函数初始化控件。 由于控件是窗口,因此该过程还可以通过使用窗口管理功能(如 EnableWindowSetFocus)对其进行操作。 该过程可以使用 GetDlgItem 函数检索控件的窗口句柄。

对话框过程可以根据需要更改任何控件的内容、状态和位置。 例如,在包含文件名列表框和 “打开 ”按钮的对话框中,该过程可以禁用“ 打开 ”按钮,直到用户从列表中选择文件。 在此示例中,对话框模板指定“打开”按钮的WS_DISABLED样式,系统会在创建按钮时自动禁用该按钮。 当对话框过程收到来自列表框的通知消息,指示用户已选择文件时,该过程将调用 EnableWindow 函数来启用 “打开 ”按钮。

若要在对话框的描述文字栏上显示自定义图标,WM_INITDIALOG处理程序可以将WM_SETICON消息发送到对话框。

如果应用程序使用函数 DialogBoxParamDialogBoxIndirectParamCreateDialogParamCreateDialogIndirectParam 之一创建对话框,则WM_INITDIALOG消息的 lParam 参数包含传递给函数的额外参数。 应用程序通常使用此额外参数将指向其他初始化信息的指针传递到对话框过程,但对话框过程必须确定参数的含义。 如果应用程序使用另一个函数创建对话框,系统会将 lParam 参数设置为 NULL

在从 WM_INITDIALOG 消息返回之前,该过程应确定是否应将输入焦点设置为指定的控件。 如果对话框过程返回 TRUE,则系统会自动将输入焦点设置为其窗口句柄位于 wParam 参数中的控件。 如果接收默认焦点的控件不合适,则可以使用 SetFocus 函数将焦点设置为适当的控件。 如果过程设置输入焦点,则必须返回 FALSE ,以防止系统设置默认焦点。 接收默认输入焦点的控件始终是模板中指定的第一个可见、未禁用且具有 WS_TABSTOP 样式的控件。 如果不存在此类控件,系统会将默认输入焦点设置为模板中的第一个控件。

WM_COMMAND消息

当用户在 控件中执行操作时,控件可以将 WM_COMMAND 消息发送到对话框过程。 这些消息称为通知消息,通知用户输入的过程,并允许它执行适当的响应。

除静态控件外,所有预定义控件都发送所选用户操作的通知消息。 例如,每当用户单击按钮时,按钮就会发送 BN_CLICKED 通知消息。 在所有情况下, wParam 参数的低序字包含控件标识符, wParam 的高序字包含通知代码, lParam 参数包含控件窗口句柄。

对话框过程应监视和处理通知消息。 具体而言,该过程必须处理具有 IDOK 或 IDCANCEL 标识符的消息;这些消息表示用户关闭对话框的请求。 该过程应通过使用用于模式对话框的 EndDialog 函数和用于无模式对话框的 DestroyWindow 函数来关闭对话框。

如果对话框具有菜单(如窗口菜单),并且用户单击菜单项,系统还会向对话框过程发送WM_COMMAND消息。 具体而言,每当用户单击对话框窗口菜单中的“关闭”时,系统会发送一条WM_COMMAND消息,其中 wParam 参数设置为 IDCANCEL。 该消息与 “取消 ”按钮发送的通知消息几乎相同,应以完全相同的方式进行处理。

WM_PARENTNOTIFY消息

当用户在指向控件时按下鼠标按钮时,控件将发送 WM_PARENTNOTIFY 消息。 某些应用程序将此消息解释为执行与控件相关的操作的信号,例如显示描述控件用途的文本行。

系统还会在创建和销毁窗口时发送 WM_PARENTNOTIFY 消息,但不会发送从对话框模板创建的控件的消息。 系统通过在创建控件时指定 WS_EX_NOPARENTNOTIFY 样式来阻止这些消息。 应用程序无法替代此默认行为,除非它为对话框创建自己的控件。

Control-Color消息

当控件和系统希望对话框过程使用特定画笔和颜色绘制控件或其他窗口的背景时,可以发送控件颜色消息。 当应用程序替代对话框及其控件中使用的默认颜色时,这非常有用。 下面是已替换 WM_CTLCOLOR 消息的控件颜色消息。

控件在绘制自己的背景之前将控件颜色消息发送到对话框过程。 消息允许过程指定要使用的画笔以及设置背景色和前景色。 该过程通过返回画笔句柄来指定画笔。 若要设置背景色和前景色,该过程将 SetBkColorSetTextColor 函数与控件的显示设备上下文结合使用。 控件颜色消息将显示设备上下文的句柄传递给消息的 wParam 参数中的过程。

如果过程不处理 WM_ERASEBKGND 消息,系统将 WM_CTLCOLORDLG消息发送到 对话框过程。 预定义的对话框类没有类背景画笔,因此此消息允许过程定义其自己的背景,而无需包含代码来执行工作。

在任何情况下,当对话框过程不处理控件颜色消息时,系统将使用具有默认窗口颜色的画笔来绘制除滚动条之外的所有控件和窗口的背景。 应用程序可以通过将 COLOR_WINDOW 值传递给 GetSysColor 函数来检索默认窗口颜色。 绘制背景时,显示设备上下文的前景色设置为默认文本颜色, (COLOR_WINDOWTEXT) 。 对于滚动条,系统使用具有默认滚动条颜色的画笔 (COLOR_SCROLLBAR) 。 在这种情况下,显示设备上下文的背景色和前景色分别设置为白色和黑色。

对话框默认消息处理

预定义对话框类的窗口过程对对话框过程不处理的所有消息执行默认处理。 当对话框过程对任何消息返回 FALSE 时,预定义的窗口过程会检查消息并执行以下默认操作:

消息 默认操作
DM_GETDEFID 可以将此消息发送到对话框。 如果对话框有一个按钮,对话框将返回默认按钮的控制标识符;否则,它将返回零。
DM_REPOSITION 可以将此消息发送到顶级对话框。 对话框会重新定位自身,使其适合桌面区域。
DM_SETDEFID 可以将此消息发送到对话框。 对话框将默认的按下按钮设置为 wParam 参数中的控件标识符指定的控件。
WM_ACTIVATE 如果激活对话框,则将输入焦点还原到以前保存的句柄标识的控件。 否则,该过程会将句柄保存到具有输入焦点的控件。
WM_CHARTOITEM 返回零。
WM_CLOSE BN_CLICKED 通知消息发布到对话框中,并将 IDCANCEL 指定为控件标识符。 如果对话框具有 IDCANCEL 控件标识符,并且当前禁用了该控件,则该过程将发出警告,并且不会发布消息。
WM_COMPAREITEM 返回零。
WM_ERASEBKGND 使用从 WM_CTLCOLORDLG 消息返回的画笔或使用默认窗口颜色填充对话框工作区。
WM_GETFONT 返回应用程序定义的对话框字体的句柄。
WM_INITDIALOG 返回零。
WM_LBUTTONDOWN CB_SHOWDROPDOWN 消息发送到具有输入焦点的组合框,指示控件隐藏其下拉列表框。 该过程调用 DefWindowProc 来完成默认操作。
WM_NCDESTROY 释放为对话框中的编辑控件分配的全局内存, (应用于指定 DS_LOCALEDIT 样式) 对话框,并释放任何应用程序定义的字体 (应用于指定 DS_SETFONTDS_SHELLFONT 样式) 的对话框。 该过程调用 DefWindowProc 函数来完成默认操作。
WM_NCLBUTTONDOWN CB_SHOWDROPDOWN 消息发送到具有输入焦点的组合框,指示控件隐藏其下拉列表框。 该过程调用 DefWindowProc 来完成默认操作。
WM_NEXTDLGCTL 将输入焦点设置为对话框中的下一个或上一个控件、 wParam 参数中的句柄标识的控件,或设置为对话框中可见、未禁用且具有 WS_TABSTOP 样式的第一个控件。 如果具有输入焦点的当前窗口不是控件,则过程将忽略此消息。
WM_SETFOCUS 将输入焦点设置为以前保存的控件窗口句柄标识的控件。 如果不存在此类句柄,该过程会将输入焦点设置为对话框模板中可见、未禁用且具有 WS_TABSTOP 样式的第一个控件。 如果不存在此类控件,该过程会将输入焦点设置为模板中的第一个控件。
WM_SHOWWINDOW 如果对话框处于隐藏状态,则保存具有输入焦点的控件的句柄,然后调用 DefWindowProc 来完成默认操作。
WM_SYSCOMMAND 如果对话框正在最小化,则保存具有输入焦点的控件的句柄,然后调用 DefWindowProc 来完成默认操作。
WM_VKEYTOITEM 返回零。

预定义的窗口过程将所有其他消息传递给 DefWindowProc 进行默认处理。

对话框键盘界面

系统为对话框提供一个特殊的键盘界面,用于对多个键执行特殊处理。 接口生成与对话框中某些按钮对应的消息,或者将输入焦点从一个控件更改为另一个控件。 下面是此接口中使用的键及其各自的操作。

密钥 操作
Alt+助记键 将输入焦点移动到第一个控件, (WS_TABSTOP样式) 包含指定助记键的静态控件之后。
DOWN 将输入焦点移动到组中的下一个控件。
Enter WM_COMMAND 消息发送到对话框过程。 wParam 参数设置为默认按钮的 IDOK 或控件标识符。
ESC WM_COMMAND 消息发送到对话框过程。 wParam 参数设置为 IDCANCEL。
LEFT 将输入焦点移动到组中的上一个控件。
记忆 将输入焦点移动到第一个控件, (WS_TABSTOP样式) 包含指定助记键的静态控件之后。
RIGHT 将输入焦点移动到组中的下一个控件。
Shift+Tab 将输入焦点移动到具有 WS_TABSTOP 样式的上一个控件。
Tab 将输入焦点移动到具有 WS_TABSTOP 样式的下一个控件。
UP 将输入焦点移动到组中的上一个控件。

系统自动为所有模式对话框提供键盘界面。 它不提供无模式对话框的接口,除非应用程序调用 IsDialogMessage 函数来筛选其main消息循环中的消息。 这意味着,应用程序必须在从消息队列中检索消息后立即将消息传递到 IsDialogMessage 。 函数处理消息(如果它用于对话框),并返回一个非零值以指示消息已处理,并且不得传递给 TranslateMessageDispatchMessage 函数。

由于对话框键盘界面使用方向键在对话框中的控件之间移动,因此应用程序不能使用这些键滚动调用 了 IsDialogMessage 的任何模式对话框或任何无模式对话框的内容。 当对话框包含滚动条时,应用程序必须为滚动条提供备用键盘界面。 请注意,当系统包含鼠标时,用于滚动的鼠标界面可用。

WS_TABSTOP样式

WS_TABSTOP样式指定用户可以通过按 TAB 键或 SHIFT+TAB 键移动到的控件。

当用户按 TAB 或 SHIFT+TAB 时,系统首先确定这些键是否由当前具有输入焦点的控件处理。 它向控件发送 WM_GETDLGCODE 消息,如果控件返回DLGC_WANTTAB,系统会将键传递给控件。 否则,系统使用 GetNextDlgTabItem 函数查找可见、未禁用且具有 WS_TABSTOP 样式的下一个控件。 搜索从当前具有输入焦点的控件开始,并按照创建控件的顺序(即在对话框模板中定义它们的顺序)继续。 当系统找到具有所需特征的控件时,系统会将输入焦点移到该控件上。

如果搜索具有 WS_TABSTOP 样式的下一个控件遇到 具有WS_EX_CONTROLPARENT 样式的窗口,则系统以递归方式搜索该窗口的子级。

应用程序还可以使用 GetNextDlgTabItem 来查找具有 WS_TABSTOP样式的 控件。 函数检索具有 WS_TABSTOP 样式的下一个或上一个控件的窗口句柄,而无需移动输入焦点。

WS_GROUP样式

默认情况下,每当用户按方向键时,系统就会将输入焦点移动到下一个或上一个控件。 只要具有输入焦点的当前控件不处理这些键,并且下一个或上一个控件不是静态控件,系统将继续在对话框中的所有控件之间移动输入焦点,因为用户继续按方向键。

应用程序可以使用 WS_GROUP 样式来修改此默认行为。 样式标记一组控件的开头。 如果用户开始按方向键时组中的控件具有输入焦点,则焦点将保留在组中。 通常,组中的第一个控件必须具有 WS_GROUP 样式,组中的所有其他控件不得具有此样式。 组中的所有控件都必须是连续的,也就是说,它们必须是连续创建的,没有干预控件。

当用户按下方向键时,系统首先确定具有输入焦点的当前控件是否处理方向键。 系统向控件发送 WM_GETDLGCODE 消息,如果控件返回 DLGC_WANTARROWS 值,请将键传递给控件。 否则,系统将使用 GetNextDlgGroupItem 函数来确定组中的下一个控件。

GetNextDlgGroupItem 按创建控件的顺序 (或反向顺序搜索控件) 。 如果用户按 RIGHT 或 DOWN 键, GetNextDlgGroupItem 将返回下一个控件(如果该控件没有 WS_GROUP 样式)。 否则,函数将反转搜索顺序,并返回具有 WS_GROUP 样式的第一个控件。 如果用户按 LEFT 或 UP 键,函数将返回上一个控件,除非当前控件已具有 WS_GROUP 样式。 如果当前控件具有此样式,则函数将反转搜索顺序,查找具有 WS_GROUP 样式的第一个控件,并返回紧靠在所定位控件之前的控件。

如果搜索组中的下一个控件遇到具有 WS_EX_CONTROLPARENT 样式的窗口,则系统以递归方式搜索该窗口的子级。

系统具有下一个或上一个控件后,它会向控件发送 WM_GETDLGCODE 消息以确定控件类型。 然后,如果输入焦点不是静态控件,系统会将输入焦点移动到控件。 如果控件是自动单选按钮,系统会向它发送 BM_CLICK 消息。 应用程序还可以使用 GetNextDlgGroupItem 查找组中的控件。

通常,组中的第一个控件将 WS_GROUPWS_TABSTOP 样式组合在一起,以便用户可以使用 TAB 键在组中移动。 如果组包含单选按钮,则应用程序应仅将 WS_TABSTOP 样式应用于组中的第一个控件。 当用户在组中的控件之间移动时,系统会自动移动样式。 这可确保当用户使用 TAB 键移动到组时,输入焦点将始终位于最近选择的控件上。

助记键

助记键是按钮标签或静态控件文本中的选定字母或数字。 每当用户按下与助记键对应的键或组合按下此键和 Alt 键时,系统就会将输入焦点移动到与助记键关联的控件上。 助记键为用户提供了一种使用键盘移动到指定控件的快速方法。

应用程序通过在控件的标签或文本中紧接选定字母或数字之前插入和 (&) ,为控件创建助记键。 在大多数情况下,对话框模板中 控件提供的以 null 结尾的字符串包含与号。 但是,应用程序可以随时通过使用 SetDlgItemText 函数替换控件的现有标签或文本来创建助记键。 每个控件只能指定一个助记键。 尽管建议使用,但对话框中的助记键不必是唯一的。

当用户按下字母或数字键时,系统首先确定具有输入焦点的当前控件是否处理该键。 系统向控件发送 WM_GETDLGCODE 消息,如果控件返回 DLGC_WANTALLKEYSDLG_WANTMESSAGE 值,则系统会将键传递给控件。 否则,它会搜索助记键与指定字母或数字匹配的控件。 它会继续搜索,直到找到控件或检查了所有控件。 在搜索期间,它会跳过具有 SS_NOPREFIX 样式的任何静态控件。

如果搜索具有匹配助记键的控件遇到具有 WS_EX_CONTROLPARENT 样式的窗口,则系统以递归方式搜索该窗口的子级。

如果系统找到静态控件且该控件未禁用,则系统会将输入焦点移动到可见、未禁用且具有 WS_TABSTOP 样式的静态控件之后的第一个控件。 如果系统找到具有匹配助记键的其他控件,则会将输入焦点移动到该控件。 如果控件是默认的按钮,系统会向对话框过程发送 BN_CLICKED 通知消息。 如果控件是另一种按钮样式,并且对话框中没有其他控件具有相同的助记键,则系统会将 BM_CLICK 消息发送到控件。

对话框设置

对话框设置是对话框中控件的当前选择和值。 创建对话框时,对话框过程负责将控件初始化为这些设置。 它还负责在销毁对话框之前从控件中检索当前设置。 用于初始化和检索设置的方法取决于控件的类型。

有关详情,请参阅以下主题:

单选按钮和复选框

对话框使用单选按钮和检查框,让用户从选项列表中进行选择。 单选按钮允许用户从互斥选项中进行选择;检查框允许用户选取选项组合。

对话框过程可以使用 CheckDlgButton 函数设置检查框的初始状态,该函数可设置或清除检查框。 对于一组互斥单选按钮中的单选按钮,对话框过程可以使用 CheckRadioButton 函数设置相应的单选按钮并自动清除任何其他单选按钮。

在对话框终止之前,对话框过程可以使用 IsDlgButtonChecked 函数检查每个单选按钮和检查框的状态,该函数返回按钮的当前状态。 对话框通常会保存此信息,以便在下次创建对话框时初始化按钮。

对话框编辑控件

许多对话框都有允许用户提供文本作为输入的编辑控件。 大多数对话框过程在对话框首次启动时初始化编辑控件。 例如,对话框过程可能会在控件中放置一个建议的文件名,然后用户可以选择、修改或替换该文件名。 对话框过程可以使用 SetDlgItemText 函数设置编辑控件中的文本,该函数将文本从指定的缓冲区复制到编辑控件。 当编辑控件收到输入焦点时,它会自动选择要编辑的完整文本。

由于编辑控件不会自动将其文本返回到对话框,因此对话框过程必须在终止前检索文本。 它可以使用 GetDlgItemText 函数检索文本,该函数将编辑控件文本复制到缓冲区。 对话框过程通常会保存此文本,以便稍后初始化编辑控件,或将其传递给父窗口进行处理。

某些对话框使用允许用户输入数字的编辑控件。 对话框过程可以使用 GetDlgItemInt 函数从编辑控件中检索数字,该函数从编辑控件中检索文本并将文本转换为十进制值。 用户以十进制数字键入数字。 它可以是有符号的,也可以是无符号的。 对话框过程可以使用 SetDlgItemInt 函数显示整数。 SetDlgItemInt 将有符号或无符号整数转换为十进制数字字符串。

列表框、组合框和目录列表

某些对话框显示用户可从中选择一个或多个名称的名称列表。 例如,为了显示文件名列表,对话框通常使用列表框以及 DlgDirListDlgDirSelectEx 函数。 DlgDirList 函数自动使用当前目录中的文件名填充列表框。 DlgDirSelect 函数从列表框中检索所选文件名。 这两个函数共同为对话框提供了一种显示目录列表的便捷方式,以便用户可以选择文件,而无需键入其名称和位置。

对话框还可以使用组合框来显示文件名列表。 DlgDirListComboBox 函数使用当前目录中的文件名自动填充组合框的列表框部分。 DlgDirSelectComboBoxEx 函数从列表框部分检索所选文件名。

对话框控制消息

许多控件可识别控件接收的预定义消息,导致它们执行某些操作。 例如,BM_SETCHECK消息在检查框中设置检查,EM_GETSEL消息将检索当前选定的控件文本部分。 与标准函数相比,控件消息为对话过程提供了对控件的更大、更灵活的访问,因此当对话框需要与用户进行复杂的交互时,通常会使用这些控件。

对话框过程可以通过提供控件标识符并使用 SendDlgItemMessage 函数(与 SendMessage 函数相同)向控件发送消息,只不过它使用控件标识符而不是窗口句柄来标识要接收消息的控件。 指定的消息可能要求对话过程使用消息发送参数,并且消息可能具有相应的返回值。 每个控件消息的操作和要求取决于消息的用途和处理它的控件。

有关控件消息的详细信息,请参阅 Windows 控件

自定义对话框

应用程序可以通过为对话框使用应用程序定义的窗口类而不是预定义对话框类来创建自定义对话框。 当对话框是其main窗口时,应用程序通常使用此方法,但对于为具有标准重叠窗口的应用程序创建模式和无模式对话框也很有用。

应用程序定义的窗口类允许应用程序定义对话框的窗口过程,并在将消息发送到对话框过程之前对其进行处理。 它还允许应用程序定义对话框的类图标、类背景画笔和类菜单。 应用程序必须在尝试创建对话框之前注册窗口类,并且必须为对话框模板提供窗口类的 atom 值或名称。

许多应用程序通过首先检索预定义对话框类的类信息并将其传递给 GetClassInfo 函数(该函数使用信息填充 WNDCLASS 结构)来创建新的对话框类。 应用程序修改结构的单个成员(如类名、画笔和图标),然后使用 RegisterClass 函数注册新类。 如果应用程序自行填充 WNDCLASS 结构,则必须将 cbWndExtra 成员设置为 DLGWINDOWEXTRA,这是系统为每个对话框所需的额外字节数。 如果应用程序还对每个对话框使用额外的字节,则它们必须超出系统所需的额外字节数。

自定义对话框的窗口过程具有与任何其他窗口过程相同的参数和要求。 但是,与其他窗口过程不同,此对话框的窗口过程应调用 DefDlgProc 函数,而不是不处理的任何消息的 DefWindowProc 函数。 DefDlgProc 执行与预定义对话框的窗口过程相同的默认消息处理,其中包括调用对话框过程。

应用程序还可以通过对预定义对话框的窗口过程进行子类化来创建自定义对话框。 SetWindowLong 函数允许应用程序指定指定窗口的窗口过程。 应用程序还可以尝试使用 SetClassLong 函数子类,但这样做会影响系统中的所有对话框,而不仅仅是属于应用程序的对话框。

创建自定义对话框的应用程序有时为对话框提供备用键盘界面。 对于无模式对话框,这可能意味着应用程序不会调用 IsDialogMessage 函数,而是在自定义窗口过程中处理所有键盘输入。 在这种情况下,应用程序可以使用 WM_NEXTDLGCTL 消息来最大程度地减少将输入焦点从一个控件移动到另一个控件所需的代码。 当此消息传递到 DefDlgProc 时,会将输入焦点移动到指定的控件并更新控件的外观,例如移动默认的按钮边框或设置自动单选按钮。