目前,我在无模式对话框中的编辑框中使用自定义窗口过程,以便向用户提供实时反馈,即,如果必填字段为空/无效,则禁用命令按钮,否则启用它以及过滤用户输入(即仅允许十六进制数字,...)
这应该在“您打字时”发生,而不是在失去或获得焦点时发生。
为了实时过滤用户输入(包括剪贴板粘贴),窗口程序是可行的方法,但是有没有其他优雅的方法至少可以处理常见情况“如果编辑框为空,则禁用“确定”按钮”,以便我可以避免为每个编辑框编写自定义窗口过程,否则会有特定的但可重用类型的过滤过程?
oldWindowProc = (WNDPROC)GetWindowLongA(hPassword, GWL_WNDPROC);
custom_user_data3 = (WNDPROC*)HeapAlloc(GetProcessHeap(), 0, sizeof(WNDPROC) + 4 * sizeof(HWND));
custom_user_data3[0] = oldWindowProc;
custom_user_data3[1] = (WNDPROC)hTestButton;
custom_user_data3[2] = (WNDPROC)hUrl;
custom_user_data3[3] = (WNDPROC)hUsername;
custom_user_data3[4] = (WNDPROC)hPassword;
SetWindowLongA(hPassword, GWL_USERDATA, (LONG)custom_user_data3);
另一个控件的 HWND 被传输(转换为 WNDPROC,但实际上是 HWND)到自定义窗口过程,因此它可以使用 EnableWindow 对它们进行操作。
在窗口内进行操作,然后我这样做:
if (msg == WM_KEYUP || msg == WM_KEYDOWN || msg == WM_CHAR)
{
// IsWindow: Precaution to not break functionality in case of renaming of controls
if (IsWindow(hwnd_testbutton) && IsWindow(hwnd_username_edit) && IsWindow(hwnd_password_edit) &&
IsWindow(hwnd_url_edit) &&
((GetWindowTextLengthA(hwnd_url_edit) <= 0) || (GetWindowTextLengthA(hwnd_username_edit) <= 0) || (GetWindowTextLengthA(hwnd_password_edit) <= 0)))
{
EnableWindow(hwnd_testbutton, false);
}
else
{
EnableWindow(hwnd_testbutton, true);
}
}
您需要的一切1已在对话框管理器中实现:每当子级编辑控件的内容发生更改时2都会向父级发送
EN_CHANGE
通知。父级是对话框,它的对话框过程在您的控制之下。
剩下的挑战是将文档提出的密码音译为人类可读的:
消息接收此通知代码。WM_COMMAND
对话框程序需要处理
WM_COMMAND
消息。参数的解码取决于此表中概述的消息源。 EN_CHANGE
通知落入由 Control
: 指定的行
以下示例说明了如何响应更改通知。它实现了一个对话框过程 (
DlgProc
),用于处理来自编辑控件 (EN_CHANGE
) 的 IDC_EDIT
通知,以切换按钮 (IDC_BTN
) 的启用状态。
main.c:
#include <Windows.h>
#include "resources.h"
#include <stdbool.h>
#pragma comment(linker, "\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")
LRESULT CALLBACK DlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_CLOSE:
// End the modal dialog loop; a modeless dialog would call
// `DestroyWindow()` instead
EndDialog(hDlg, 0);
return TRUE;
case WM_COMMAND:
// Filter on `EN_CHANGE` notifications sent from the `IDC_EDIT` control
if (LOWORD(wParam) == IDC_EDIT && HIWORD(wParam) == EN_CHANGE)
{
// Determine whether the edit control contains any text
HWND hwnd_edit = (HWND)lParam;
bool non_empty = GetWindowTextLengthW(hwnd_edit) > 0;
// En-/disable `IDC_BTN` accordingly
HWND hwnd_btn = GetDlgItem(hDlg, IDC_BTN);
EnableWindow(hwnd_btn, non_empty);
// Message handled
return TRUE;
}
break;
default:
break;
}
// Default processing
return FALSE;
}
int APIENTRY wWinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PWSTR cmdline, int cmdshow)
{
return (int)DialogBoxW(hInst, MAKEINTRESOURCEW(IDD_MAIN), NULL, DlgProc);
}
资源.h:
#pragma once
#define IDD_MAIN 101
#define IDC_EDIT 1001
#define IDC_BTN 1002
资源.rc:
#include "winres.h"
#include "resources.h"
IDD_MAIN DIALOGEX 0, 0, 160, 40
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "EN_CHANGE Demo"
FONT 9, "Segoe UI", 0, 0, 0x0
BEGIN
EDITTEXT IDC_EDIT 10, 10, 100, 20
PUSHBUTTON "Test", IDC_BTN 115, 10, 35, 20, WS_DISABLED
END
这相当简单:入口点会启动一个在“资源脚本”中声明的模式对话框。唯一值得一提的是按钮(IDC_BTN
)有
WS_DISABLED
窗口样式设置。这与编辑控件 (
IDC_EDIT
) 默认不保留文本相关。