我正在研究一个C ++ winapi应用程序,我正在努力让SetFocus()
为我工作。我使用WS_OVERLAPPEDWINDOW | WS_VISIBLE
创建主窗口,然后在其中使用WS_CHILD | WS_VISIBLE
创建其子项(几个按钮和我自己版本的Edit控件的开头),除焦点问题外,它们都正常工作。
在我的搜索中,我一直在努力寻找你应该如何处理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,我在这里处理消息。
所以有人对我有任何想法吗?
如果我正在谈论这是完全错误的方式,或者如果我没有遵循最佳实践请说出来,我这样做是为了学习所以拍摄。
[UPDATE]
好的,这里有一些代码来演示这个问题:
#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;
}
#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);
}
#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;
}
#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()
然后点击确实改变了焦点。
尽管最初设置焦点仍然存在问题。在启动应用程序后,自定义编辑控件仍然显示插入符号,即使它没有焦点,您需要单击它以使其聚焦,从而它将接收键盘输入。之后焦点按照需要工作:如果我点击主窗口它有焦点;如果我点击自定义编辑,它有焦点等。
好吧所以事实证明我的整个问题都与该行撒谎SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
一旦我切换了这个:
case WM_LBUTTONDOWN:
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
SetFocus(hwnd);
break;
至:
case WM_LBUTTONDOWN:
SetFocus(hwnd);
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
break;
一切都在一起。