I've got a problem with an invalidly drawn ComboBox using WinAPI. When you minimize the application and resume it after the selection of the ComboBox control is not hidden, it looks like this:
As you can see the OK button has the focus but yet the ComboBox's selection is still not hidden. The normal behavior of the ComboBox hides the selection when the control loses the input focus.
Code:
#define WIN32_MEAN_AND_LEAN
#include <SDKDDKVer.h>
#include <Windows.h>
#include <Windowsx.h>
#include <CommCtrl.h>
#include <assert.h>
struct window_context {
HINSTANCE _instance;
HWND _window;
HWND _combo_box2;
HWND _ok_button;
window_context(HINSTANCE instance) noexcept : _instance{ instance }
{
}
};
static BOOL on_create(HWND hwnd, LPCREATESTRUCT lpCreateStruct) noexcept
{
auto context = reinterpret_cast<window_context*>(lpCreateStruct->lpCreateParams);
context->_combo_box2 = CreateWindowW(WC_COMBOBOXW,
L"",
CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_TABSTOP | WS_VSCROLL | WS_VISIBLE | WS_CHILD,
0, 0, 0, 0,
hwnd,
(HMENU)44,
nullptr,
nullptr);
ComboBox_AddString(context->_combo_box2, L"select me");
context->_ok_button = CreateWindowW(WC_BUTTONW,
L"OK",
BS_DEFPUSHBUTTON | BS_NOTIFY | WS_TABSTOP | WS_VISIBLE | WS_CHILD,
0, 0, 0, 0,
hwnd,
nullptr,
nullptr,
nullptr);
return true;
}
static void on_destroy(HWND hwnd) noexcept
{
PostQuitMessage(0);
}
static void on_size(HWND hwnd, UINT state, int cx, int cy) noexcept
{
auto context = reinterpret_cast<window_context*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
MoveWindow(context->_combo_box2,
10,
10,
100,
100,
true); // causes the weird behaviour to occur
// using SetWindowPos doesn't help
// using SetWindowPos with SWP_NOSIZE prevents the bug from occuring
// but also locks it into size which is not acceptable
// since I use this to deal with DPI scaling changes
MoveWindow(context->_ok_button,
10,
110,
100,
50,
true);
}
static void on_activate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized) noexcept
{
auto context = reinterpret_cast<window_context*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
if (state)
SetFocus(context->_ok_button);
}
static BOOL on_nc_create(HWND hwnd, LPCREATESTRUCT lpCreateStruct) noexcept
{
auto context = reinterpret_cast<window_context*>(lpCreateStruct->lpCreateParams);
context->_window = hwnd;
SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(context));
return FORWARD_WM_NCCREATE(hwnd, lpCreateStruct, DefWindowProcW);
}
static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept
{
switch (uMsg) {
HANDLE_MSG(hwnd, WM_CREATE, on_create);
HANDLE_MSG(hwnd, WM_DESTROY, on_destroy);
HANDLE_MSG(hwnd, WM_SIZE, on_size);
HANDLE_MSG(hwnd, WM_ACTIVATE, on_activate);
HANDLE_MSG(hwnd, WM_NCCREATE, on_nc_create);
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
static bool register_class(HINSTANCE hInstance) noexcept
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = &wnd_proc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = L"test";
wcex.hIconSm = nullptr;
return RegisterClassExW(&wcex) != 0;
}
static bool create_window(window_context& context) noexcept
{
return CreateWindowExW(0,
L"test",
L"Win32 Program",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
nullptr,
nullptr,
context._instance,
&context);
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
if (!register_class(hInstance))
return false;
window_context context{ hInstance };
if (!create_window(context))
return false;
ShowWindow(context._window, nCmdShow);
MSG msg;
BOOL got_message;
while ((got_message = GetMessageW(&msg, nullptr, 0, 0)) && got_message != -1) {
if (IsDialogMessageW(context._window, &msg))
continue;
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return static_cast<int>(msg.wParam);
}
Steps to reproduce the problem:
- select
select mefrom the ComboBox - minimize the window
- restore the window
The ComboBox should now appear selected even though it is not.
Steps taken to fix the problem:
- Invalidating the ComboBox's area does not help, redrawing the combobox still doesn't fix the issue
- The issue does not appear when you just create the ComboBox at a specified place and never move its position or change its size
- The issue does not appear when you just move the ComboBox's position using
SetWindowPos - As soon as you change the size of the ComboBox, this weird behaviour appears.
- Dialogs somehow handle this properly
- WinForms handles this properly aswell but I failed to find what they do to prevent it
- The bug only appears if you selected an item from dropdown list, when you type in custom text the bug disappears
Surely enough this is a highly exotic problem, I'm very thankful for any help.

WM_ACTIVATEat all? Let Windows take care of restoring the focus. For that you should propably subclass the dialog window class (WC_DIALOGor "#32770") whose defaultWM_ACTIVATEshould do the right thing. Last, usingSetFocus()in a dialog is almost always wrong, because it doesn't care about default button state. UseWM_NEXTDLGCTLinstead. - zett42