Winapi C ++ Handling Focus

Хорошо, я попытаюсь объяснить это, не став эссе, но есть много деталей, которые я считаю важными, поэтому, пожалуйста, держитесь за меня.

В качестве стороннего проекта я работаю над приложением 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, где я обрабатываю сообщения по мере их поступления.

Так у кого-нибудь есть мысли для меня?

Если я говорю об этом совершенно неправильно, или если я не следую наилучшей практике, скажите, пожалуйста, я делаю это, чтобы научиться, так что избегайте.

[ОБНОВИТЬ]

Хорошо, вот код для демонстрации проблемы:

main.cpp

#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;
}

CustomTextBox.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() Сначала щелчок меняет фокус.

Тем не менее, существует проблема с первоначальной настройкой фокуса. В момент, после запуска приложения, пользовательский элемент управления редактирования по-прежнему отображает каретку, даже если она не имеет фокуса, и вам нужно щелкнуть по нему, чтобы выделить его, в результате чего он получит ввод с клавиатуры. После этого фокус работает как нужно: если я щелкаю по главному окну, оно имеет фокус; если я нажимаю на пользовательское редактирование, оно имеет фокус и т. д.

3

Решение

Хорошо, так получается, что вся моя проблема заключается в этой строке 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;

все сошлись.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]