After investigation, I still consider this a Windows 11 bug, or a break in compatibility, which amounts to the same thing. I didn't find other occurrences of this exact issue, but I found a couple of other ComboBox rendering issues caused by Windows 11 (also seemingly related to painting - or not painting - the background):
ComboBox ghost after Windows 11 upgrade - Microsoft Tech Community
ComboBoxGadgets do not render correctly on Windows 11 when a window background color is set - PureBasic Forums - English
The transparency effect is achieved by a WM_ERASEBKGND message being sent to the parent window to erase the region occupied by the control before it repaints its content. WM_ERASEBKGND is also the means by which the window's background is customized. On all previous versions of Windows, the ComboBox paints its own background and ignores the window's background.
The following workaround can be used to prevent the black background from showing behind the control, instead painting it some other colour:
Code: Select all
#Requires AutoHotkey v1.1
OnMessage(0x14, "Erase")
Erase(hdc, lParam, nmsg, hgui) {
if !A_Gui
return
Loop {
GuiControlGet hctrl, Hwnd, ComboBox%A_Index%
if !hctrl
break
if !DllCall("uxtheme\GetWindowTheme", "ptr", hctrl)
continue ; Skip controls with -Theme.
VarSetCapacity(rect, 16, 0)
; Get control rectangle in client coordinates.
DllCall("GetWindowRect", "ptr", hctrl, "ptr", &rect)
DllCall("MapWindowPoints", "ptr", 0, "ptr", hgui, "ptr", &rect, "int", 2)
; Shrink inward by 1 pixel to avoid white dots in corners.
DllCall("InflateRect", "ptr", &rect, "int", -1, "int", -1)
if !DllCall("RectVisible", "ptr", hdc, "ptr", &rect)
continue ; Control is not inside clipping region.
; Fill control background with COLOR_WINDOW (seems to match the Edit).
static hbrush := DllCall("GetSysColorBrush", "int", 5, "ptr")
DllCall("FillRect", "ptr", hdc, "ptr", &rect, "ptr", hbrush)
; Exclude the control from default handling of WM_ERASEBKGND.
DllCall("ExcludeClipRect", "ptr", hdc
, "int", NumGet(rect, 0, "int"), "int", NumGet(rect, 4, "int")
, "int", NumGet(rect, 8, "int"), "int", NumGet(rect, 12, "int"))
}
}
Gui, -MinimizeBox
Gui, Color, Black
Gui, Add, DropDownList, w100 h100, 1|2|3|4|5
Gui, Add, DropDownList, w100 h100 -Theme, 1|2|3|4|5
Gui, Add, ComboBox, w100 h100, 1|2|3|4|5
Gui, Show
Return
Esc::
GuiClose:
ExitApp
Code: Select all
#Requires AutoHotkey v2.0-
OnMessage(0x14, Erase)
Erase(hdc, lParam, nmsg, hgui) {
if !g := GuiFromHwnd(hGui)
return
Loop {
try hctrl := g['ComboBox' A_Index].hwnd
catch
break
if !DllCall("uxtheme\GetWindowTheme", "ptr", hctrl)
continue ; Skip controls with -Theme.
rect := Buffer(16, 0)
; Get control rectangle in client coordinates.
DllCall("GetWindowRect", "ptr", hctrl, "ptr", rect)
DllCall("MapWindowPoints", "ptr", 0, "ptr", hgui, "ptr", rect, "int", 2)
; Shrink inward by 1 pixel to avoid white dots in corners.
DllCall("InflateRect", "ptr", rect, "int", -1, "int", -1)
if !DllCall("RectVisible", "ptr", hdc, "ptr", rect)
continue ; Control is not inside clipping region.
; Fill control background with COLOR_WINDOW (seems to match the Edit).
static hbrush := DllCall("GetSysColorBrush", "int", 5, "ptr")
DllCall("FillRect", "ptr", hdc, "ptr", rect, "ptr", hbrush)
; Exclude the control from default handling of WM_ERASEBKGND.
DllCall("ExcludeClipRect", "ptr", hdc
, "int", NumGet(rect, 0, "int"), "int", NumGet(rect, 4, "int")
, "int", NumGet(rect, 8, "int"), "int", NumGet(rect, 12, "int"))
}
}
g := Gui('-MinimizeBox')
g.BackColor := "Black"
g.AddDDL('w100 h100', [1,2,3,4,5])
g.AddDDL('w100 h100 -Theme', [1,2,3,4,5])
g.AddComboBox('w100 h100', [1,2,3,4,5])
g.OnEvent('Escape', g => g.Destroy())
g.Show()
I am not inclined to do this by default due to complexity, and doubts about how it will affect any other version of Windows, including future versions. It may also be undesirable on Windows 11 in scripts which use a non-default light background.