【发布时间】:2016-03-01 08:30:28
【问题描述】:
有没有办法在 WinAPI 中的按钮(button 窗口类)上设置真正的完全焦点?
SetFocus() 有点设置焦点(按钮获得内部虚线边框),但按钮实际上是部分焦点的,仍然无法使用Enter按下键,只有Spacebar 键有效。同时,如果我使用Tab 键将焦点移动到同级按钮,那么这个同级按钮(以及如果我随后使用Shift+Tab 将焦点返回到它的第一个按钮)将获得 true 焦点(视觉上,不只是 inner 点状焦点边框被添加到真正的焦点按钮,而是它的主要外边框变为 蓝色 [Windows 7]),现在它会按预期对Enter 做出反应。
如何使按钮以编程方式完全聚焦?
三个按钮状态的截图:
为了清晰起见的一些背景:有一个带有多行编辑框的窗口(使用WNDCLASSEX / RegisterClassEx / CreateWindowEx() 和WS_OVERLAPPEDWINDOW 作为其样式的常规组合创建)和一个多行编辑框(edit 窗口类ES_MULTILINE 风格)和几个按钮。为了使用Tab 键实现键盘导航,我在编辑框的过程中处理WM_KEYDOWN 事件(通过SetWindowLong() 子类化),否则我可以在按钮之间以及从按钮到编辑框之间导航,但不能从编辑中导航框到一个按钮。所有控件都有WS_TABSTOP 样式。当我在具有焦点和插入符号的编辑框上按下 Tab 键时,使用 SetFocus() 设置焦点时,会出现按钮焦点问题。
最少的相关 C++ 代码:
HWND mainWindow;
WNDPROC defaultEditCallback = NULL;
int WINAPI WinMain(HINSTANCE instance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEXW wc;
wchar_t windowClass[] = L"testcase";
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = 0;
wc.lpfnWndProc = mainWindowCallback;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = instance;
wc.hIcon = NULL;
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = (LPCWSTR)windowClass;
wc.hIconSm = NULL;
RegisterClassExW(&wc);
mainWindow = CreateWindowW(
(LPCWSTR)windowClass, (LPCWSTR)windowClass, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 200, NULL, NULL, instance, NULL
);
HWND edit = CreateWindowExW(
WS_EX_CLIENTEDGE, (LPCWSTR)L"edit", NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | WS_TABSTOP,
0, 0, 0, 0, mainWindow, (HMENU) 10,
(HINSTANCE) GetWindowLongPtrW(mainWindow, GWLP_HINSTANCE),
NULL
);
defaultEditCallback = (WNDPROC)SetWindowLongPtrW(edit, GWLP_WNDPROC, (LONG)editCallback);
HWND firstButton = createButton(mainWindow, 20, L"First", buttonWidth, buttonHeight);
HWND secondButton = createButton(mainWindow, 30, L"Second", buttonWidth, buttonHeight);
HWND thirdButton = createButton(mainWindow, 40, L"Third", buttonWidth, buttonHeight);
// [Skipped] Sizing and positioning controls.
ShowWindow(mainWindow, nCmdShow);
UpdateWindow(mainWindow);
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
if (!IsDialogMessage(mainWindow, &msg)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
return (int)msg.wParam;
}
LRESULT CALLBACK mainWindowCallback(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
if (WM_DESTROY == msg) {
PostQuitMessage(0);
}
return DefWindowProcW(window, msg, wParam, lParam);
}
LRESULT CALLBACK editCallback(HWND control, UINT msg, WPARAM wParam, LPARAM lParam) {
if (WM_KEYDOWN == msg && VK_TAB == wParam) {
HWND next = GetNextDlgTabItem(mainWindow, control, (int)(GetKeyState(VK_SHIFT) & 0x8000));
SetFocus(next);
return 0;
}
return CallWindowProc(defaultEditCallback, control, msg, wParam, lParam);
}
HWND createButton(HWND parentWindow, int id, wchar_t* caption, int width, int height) {
return CreateWindowW(
(LPCWSTR)L"button", (LPCWSTR)caption, WS_VISIBLE | WS_CHILD | WS_TABSTOP,
0, 0, width, height, parentWindow, (HMENU)id, NULL, NULL
);
}
谢谢。
【问题讨论】:
-
我们看不到您的代码,但没有什么“全焦点”不存在。只有控件有焦点或没有焦点。
-
谢谢,我不知道为什么状态二(部分专注,
SetFocus()的实际结果)与状态三(完全专注,预期/期望的结果)不同。就外观和行为而言,差异本身当然存在,这就是我在这里提出问题的原因。 -
没有“部分专注”的东西。您看到的是使用 DrawFocusRect() 绘制的按钮,没有视觉样式。再次没有人看到你的代码!
-
@JonathanPotter 谢谢,但我有多个按钮,没有一个应该是默认的。或者您的意思是同时动态地使每个焦点按钮成为默认按钮?在发布问题之前,我认为这是一种可能的解决方法。或者这是在按钮之间切换时自然实现的方式?
-
焦点和默认按钮齐头并进,但它们不是一回事。具有焦点的按钮将获得键盘输入 - 例如空格键将激活它。默认按钮是在您按下返回时激活的按钮。对话框本身管理默认按钮,这就是为什么如果要更改它,需要将
DM_SETDEFID发送到对话框的原因。这不是“解决方法”,而是您应该这样做的方式。