Хорошо, я попытаюсь объяснить это, не став эссе, но есть много деталей, которые я считаю важными, поэтому, пожалуйста, держитесь за меня.
В качестве стороннего проекта я работаю над приложением Winapi на C ++. Я никогда раньше не занимался программированием на winapi, так что это довольно сложная задача.
На данный момент я изо всех сил пытаюсь получить SetFocus()
работать на меня. Я создаю Главное окно с WS_OVERLAPPEDWINDOW | WS_VISIBLE
и затем внутри него я создаю его дочерние элементы (пару кнопок и начало моей собственной версии элемента управления Edit) с WS_CHILD | WS_VISIBLE
, которые все работают нормально, кроме вопроса фокуса.
В своих поисках я боролся за то, чтобы найти многое с точки зрения того, как вы должны обращаться с Фокусом. Когда все окна созданы, они по отдельности получают 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;
}
#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;
все сошлись.
Других решений пока нет …