I'm trying to create splitter 'windows' and have a provisional splitter where I'm using WM_NCHITTEST
to return HTCAPTION
and subsequently restricting axial movement by fixing the X or Y coordinates in WM_WINDOWPOSCHANING
. The splitter is a child of the main window.
The problem I'm having however is that when moving the splitter the borders smear. I can't produce a gif because the smearing effect isn't picked up, but it can be seen on any draggable child window (e.g. Open MMC, 'restore' the MDI child window and drag it around whilst observing the borders).
I need to overcome this issue for the splitters to work properly so I'm wondering how this issue might be addressed? An alternative I suppose would be to take a similar approach to this, but make the splitter control invisible. The border issue would still stand though.
Minimum reproducible example:
#include "framework.h"
#include "painttest.h"
#define MAX_LOADSTRING 100
HINSTANCE hInst;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK Bproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_PAINTTEST, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PAINTTEST));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
WNDPROC g_ogp;
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = 0;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PAINTTEST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PAINTTEST);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_CREATE:
{
HWND t = CreateWindowEx(0, L"BUTTON", L"TEST", WS_CHILD | WS_VISIBLE, 20, 20, 100, 20, hWnd, 0, hInst, 0);
g_ogp = (WNDPROC)SetWindowLongPtr(t, GWLP_WNDPROC, (UINT_PTR)Bproc);
break;
}
case WM_PAINT:
{
PAINTSTRUCT paint_structure;
RECT client_rect;
HDC paint_device_context, paint_dc;
HBITMAP bitmap;
paint_device_context = BeginPaint(hWnd, &paint_structure);
//FillRect(paint_device_context, &paint_structure.rcPaint,(HBRUSH)COLOR_WINDOW);
EndPaint(hWnd, &paint_structure);
}
break;
case WM_ERASEBKGND:
return 1;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
LRESULT CALLBACK Bproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_NCHITTEST:
{
return HTCAPTION;
}
default:
return CallWindowProc(g_ogp, hWnd, message, wParam, lParam);
}
return CallWindowProc(g_ogp, hWnd, message, wParam, lParam);
}
Notice that when moving the button, the border sears even when the parent background isn't being painted.