This is how I'm triggering the titlebar redraw after calling DwmSetWindowAttribute
on Windows 10 (not yet tested on Windows 11). As mentioned by OP none of the usual methods for triggering WM_NCPAINT seem to work. This approach works by changing and then restoring the window width. It does so in the least visible way possible, and works for both normal and maximized windows. It makes the window width smaller instead of larger to avoid potential overflow onto a secondary monitor.
if (IsWindowVisible(hwnd) && !IsIconic(hwnd)) {
// Get the current window rect.
RECT rect = {};
::GetWindowRect(hwnd, &rect);
if (IsZoomed(hwnd)) {
// Window is currently maximized.
WINDOWPLACEMENT placement = {};
GetWindowPlacement(hwnd, &placement);
// Remember the old restore rect.
const RECT oldrect = placement.rcNormalPosition;
// Change the restore rect to almost the same as the current
// maximized rect.
placement.rcNormalPosition = rect;
placement.rcNormalPosition.right -= 1;
SetWindowPlacement(hwnd, &placement);
// Restore and then re-maximize the window. Don't update in-between.
LockWindowUpdate(hwnd);
ShowWindow(hwnd, SW_SHOWNORMAL);
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
LockWindowUpdate(nullptr);
// Put back the old restore rect.
placement.rcNormalPosition = oldrect;
SetWindowPlacement(hwnd, &placement);
} else {
// Window is currently normal. Change and then restore the window width.
// Use Defer functions to make the change less noticeable. Don't update
// in-between.
HDWP defer = BeginDeferWindowPos(2);
DeferWindowPos(defer, hwnd, NULL, 0, 0,
rect.right - rect.left - 1, rect.bottom - rect.top,
SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
DeferWindowPos(defer, hwnd, NULL, 0, 0, rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
LockWindowUpdate(hwnd);
EndDeferWindowPos(defer);
LockWindowUpdate(nullptr);
}
}
SetWindowPos
? – Harvey