我正在开发一个 C++ winapi 应用程序,我正在努力SetFocus()
为我工作。我创建主窗口,WS_OVERLAPPEDWINDOW | WS_VISIBLE
然后在其中创建它的子窗口(几个按钮和我自己的编辑控件版本的开头)WS_CHILD | WS_VISIBLE
,除了焦点问题之外,它们都工作正常。
在我的搜索中,我一直在努力寻找有关如何处理 Focus 的内容。当所有窗口都被创建时,它们会单独接收WM_SETFOCUS
消息,我通过创建插入符号在我的编辑控件中处理此消息,但是似乎孩子们从未收到WM_KILLFOCUS
消息,因此插入符号永远不会被破坏。
这就是我的问题所在:我希望主父窗口最初具有焦点,并且在我的编辑控件中没有插入符号,然后当单击子编辑控件时它具有焦点,然后当主窗口单击它应该然后再次获得焦点,依此类推。
所以我最初的想法是SetFocus()
在处理消息时将焦点设置到主窗口,WM_CREATE
但这似乎不起作用:孩子没有收到WM_KILLFOCUS
消息。
我的下一个想法是,也许父母必须处理传递WM_KILLFOCUS
给适当的孩子,所以我写了一个方法来为我做这件事,但孩子们仍然没有收到WM_KILLFOCUS
消息。
所以我最好的猜测是我没有在我的 WndProc 中正确处理消息。
我创建了自己的 Window 类,并通过以下 WndProc 将消息分发到适当的类:
LRESULT CALLBACK CBaseWindow::stWinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CBaseWindow* pWnd;
if (uMsg == WM_NCCREATE)
{
SetWindowLong(hwnd, GWL_USERDATA, (long)((LPCREATESTRUCT(lParam))->lpCreateParams));
}
pWnd = GetObjectFromWindow(hwnd);
if (pWnd)
return pWnd->WinMsgHandler(hwnd, uMsg, wParam, lParam);
else
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
然后每个类都有自己的 WndProc,当消息进来时我会在其中处理它们。
那么有人对我有什么想法吗?
如果我打算这样做是完全错误的方式,或者如果我没有遵循最佳实践,请说出来,我这样做是为了学习,所以开枪。
[更新]
好的,这里有一些代码来演示这个问题:
主文件
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "MainWnd.h"
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MainWnd wnd(hInstance);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0 ) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
BaseWindow.cpp
#include "BaseWindow.h"
//...
LRESULT CALLBACK CBaseWindow::stWinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CBaseWindow* pWnd;
if (uMsg == WM_NCCREATE)
{
SetWindowLong(hwnd,
GWL_USERDATA,
(long)((LPCREATESTRUCT(lParam))->lpCreateParams));
}
pWnd = GetObjectFromWindow(hwnd);
if (pWnd)
return pWnd->WinMsgHandler(hwnd, uMsg, wParam, lParam);
else
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
BOOL CBaseWindow::Create(DWORD dwStyles, RECT* rect)
{
m_hwnd = CreateWindow(
szClassName,
szWindowTitle,
dwStyles,
rect->left,
rect->top,
rect->right - rect->left,
rect->bottom - rect->top,
NULL,
NULL,
hInstance,
(void *)this);
return (m_hwnd != NULL);
}
MainWnd.cpp
#include "MainWnd.h"
#define WIDTH 400
#define HEIGHT 400
MainWnd::MainWnd(HINSTANCE hInst): CBaseWindow(hInst), hInstance(hInst)
{
SetWindowTitle(_T("Main Window"));
WNDCLASSEX wcx;
FillWindowClass(&wcx);
if(RegisterWindow(&wcx))
{
RECT rc;
BuildRect(&rc);
if(Create(WS_OVERLAPPEDWINDOW | WS_VISIBLE, &rc))
{
customTextBox = new CustomTextBox(hInst, m_hwnd);
}
}
}
void MainWnd::FillWindowClass(WNDCLASSEX *wcx)
{
wcx->cbSize = sizeof(WNDCLASSEX);
wcx->style = CS_HREDRAW | CS_VREDRAW | CS_DROPSHADOW;
wcx->lpfnWndProc = CBaseWindow::stWinMsgHandler;
wcx->cbClsExtra = 0;
wcx->cbWndExtra = 0;
wcx->hInstance = hInstance;
wcx->hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx->hCursor = LoadCursor(NULL, IDC_ARROW);
wcx->hbrBackground = CreateSolidBrush(RGB(255,255,255));
wcx->lpszMenuName = NULL;
wcx->lpszClassName = _T("MainWindow");
wcx->hIconSm = LoadIcon(NULL, IDI_APPLICATION);
}
LRESULT CALLBACK MainWnd::WinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
delete customTextBox;
PostQuitMessage(0);
break;
case WM_LBUTTONUP:
SetFocus(hwnd);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
自定义文本框.cpp
#include "CustomTextBox.h"
CustomTextBox::CustomTextBox(
HINSTANCE hInst,
HWND hParent): CBaseWindow(hInst),
hParent(hParent),
{
WNDCLASSEX wcx;
CreateWndClassEX(wcx);
if(RegisterWindow(&wcx))
{
RECT clientRect;
CreateClientRect(clientRect);
CreateChild(WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP, &clientRect, hParent);
}
}
void CustomTextBox::CreateWndClassEX(WNDCLASSEX& wcx)
{
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = CBaseWindow::stWinMsgHandler;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = CreateSolidBrush(RGB(255,255,255));
wcx.lpszMenuName = NULL;
wcx.lpszClassName = _T("Edit Control");
wcx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
}
LRESULT CALLBACK CustomTextBox::WinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch(uMsg)
{
/* Handling the caret */
case WM_SETFOCUS:
CreateCaret(hwnd, NULL, 0, nWindowY);
SetCaretPos(GetEndOfLinePoint(), nCaretPosY * nCharY);
ShowCaret(hwnd);
return 0;
case WM_MOUSEACTIVATE:
SetFocus(hwnd);
return MA_ACTIVATE;
case WM_KILLFOCUS:
DestroyCaret();
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
发现
在编写此代码时,我遇到了问题的原因之一:在我的实际应用程序中,我没有标题栏,因此要将发送WM_NCLBUTTONDOWN
到主窗口的窗口移动到WM_LBUTTONDOWN
:
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
然后我SetFocus()
在这之后有了我的,这不起作用,但如果我切换它们并处理第SetFocus()
一个,那么点击确实会改变焦点。
但是,最初设置焦点仍然存在问题。在启动应用程序后的那一刻,自定义编辑控件仍然显示插入符号,即使它没有焦点,您需要单击它以使其获得焦点,从而它将接收键盘输入。之后,焦点按需要工作:如果我点击主窗口,它就有焦点;如果我点击自定义编辑,它有焦点等。