Je travaille sur une application winapi C++ et je me bats pour obtenir SetFocus()
pour travailler pour moi. Je crée la fenêtre principale avec WS_OVERLAPPEDWINDOW | WS_VISIBLE
puis, à l'intérieur de celui-ci, je crée ses enfants (quelques boutons et le début de ma propre version d'un contrôle d'édition) avec WS_CHILD | WS_VISIBLE
qui fonctionnent bien, à l'exception du problème de la mise au point.
Dans mes recherches, je n'ai pas trouvé grand-chose sur la façon de gérer Focus. Lorsque les fenêtres sont toutes créées, elles reçoivent individuellement la commande WM_SETFOCUS
et je traite ce message dans mon contrôle d'édition en créant le caret, mais il semble que les enfants ne reçoivent jamais le message WM_KILLFOCUS
et donc le caret n'est jamais détruit.
C'est maintenant que mon problème se pose : Je voudrais que la fenêtre principale ait initialement le focus et qu'il n'y ait pas de caret dans mon contrôle d'édition, puis que lorsque l'on clique sur le contrôle d'édition enfant, il ait le focus et que lorsque l'on clique sur la fenêtre principale, il ait à nouveau le focus et ainsi de suite.
Ma première idée était donc d'utiliser SetFocus()
pour mettre l'accent sur la fenêtre principale lors du traitement de la commande WM_CREATE
mais cela ne semble pas fonctionner : l'enfant n'a pas reçu le message WM_KILLFOCUS
message.
J'ai ensuite pensé que les parents devaient peut-être s'occuper de la transmission. WM_KILLFOCUS
aux enfants concernés. J'ai donc écrit une méthode pour le faire à ma place, mais les enfants n'ont toujours pas reçu l'information. WM_KILLFOCUS
message.
Mon hypothèse la plus probable est que je ne traite pas les messages correctement dans mon WndProc.
J'ai créé ma propre classe Window et distribue les messages aux classes appropriées par le biais du WndProc suivant :
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);
}
Ensuite, chaque classe a son propre WndProc où je traite les messages au fur et à mesure qu'ils arrivent.
Quelqu'un a-t-il des idées à me proposer ?
Si je ne m'y prends pas du tout de la bonne manière ou si je ne suis pas les meilleures pratiques, dites-le moi, je fais cela pour apprendre, alors tirez-en parti.
[MISE À JOUR]
OK, voici un peu de code pour démontrer le problème :
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);
}
}
Découverte
En rédigeant ce code, j'ai découvert l'une des causes de mes problèmes : dans mon application actuelle, je n'ai pas de barre de titre et, pour déplacer la fenêtre, j'envoyais le code suivant WM_NCLBUTTONDOWN
à ma fenêtre principale sur WM_LBUTTONDOWN
:
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
J'ai ensuite eu mon SetFocus()
après ça et ça ne marchait pas mais si je les échange et que je gère les SetFocus()
d'abord, puis le clic change la mise au point.
Cependant, le réglage initial de la mise au point pose toujours un problème. Pour l'instant, après le démarrage de l'application, le contrôle d'édition personnalisé affiche toujours le signe d'insertion même s'il n'a pas le focus et vous devez cliquer dessus pour lui donner le focus, ce qui lui permettra de recevoir les entrées du clavier. Après cela, le focus fonctionne comme souhaité : si je clique sur la fenêtre principale, elle a le focus ; si je clique sur le contrôle d'édition personnalisé, il a le focus, etc.