主窗口最小化时自绘控件重叠问题求助
问题分析与解决
核心问题
你现在的错误在于把两个控件的绘制逻辑混在了同一个WM_DRAWITEM消息处理里,而且静态背景的绘制方式存在两个关键问题:
- 每个自绘控件触发WM_DRAWITEM时,你都会重新绘制按钮和背景,导致绘制顺序完全混乱——比如静态控件触发消息时,背景会在按钮之后被绘制,直接覆盖按钮;
- 绘制背景时没有设置透明填充画刷,RoundRect默认会用当前画刷填充整个区域,把下方的按钮盖住;
- 坐标处理冗余且容易出错,用GetWindowRect再转坐标完全没必要,直接用控件的客户区坐标即可。
修复步骤
1. 拆分WM_DRAWITEM的处理逻辑
WM_DRAWITEM是每个自绘控件单独触发的消息,wParam就是控件ID,你需要根据ID分别处理按钮和静态背景的绘制,不要混在一起:
case WM_DRAWITEM: { UINT ctrlID = (UINT)wParam; LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam; if (ctrlID == IDC_YOUR_BUTTON) { // 替换成你的按钮实际ID self->OnDrawItem(lParam, wParam); // 仅处理当前按钮的绘制 } else if (ctrlID == IDC_STATIC_FOOTER) { // 单独处理静态背景绘制 HDC hdc = lpDIS->hDC; // 直接使用消息提供的DC,无需自行调用GetDC HPEN borderPen = CreatePen(PS_SOLID, 1, RGB(169, 169, 169)); HBRUSH transparentBrush = (HBRUSH)GetStockObject(NULL_BRUSH); // 透明画刷,避免填充覆盖下层控件 // 保存原有GDI对象,用完必须恢复,防止资源泄漏 HPEN oldPen = (HPEN)SelectObject(hdc, borderPen); HBRUSH oldBrush = (HBRUSH)SelectObject(hdc, transparentBrush); // 直接使用lpDIS提供的控件客户区矩形,无需自行获取坐标 RoundRect(hdc, lpDIS->rcItem.left, lpDIS->rcItem.top, lpDIS->rcItem.right, lpDIS->rcItem.bottom, 5, 5); // 恢复系统原有GDI对象 SelectObject(hdc, oldPen); SelectObject(hdc, oldBrush); DeleteObject(borderPen); // 自定义画笔需要销毁,系统画刷无需处理 } return TRUE; }
2. 关键修正点说明
- 使用消息自带的DC:
lpDIS->hDC是系统为当前控件绘制准备的DC,比自行调用GetDC更安全,也避免额外的DC管理开销; - 透明画刷:
NULL_BRUSH是系统提供的透明画刷,RoundRect使用它时只会绘制圆角边框,不会填充内部区域,彻底避免覆盖下方按钮; - 按控件ID拆分逻辑:每个控件只处理自身的绘制,确保绘制顺序和控件的Z-order一致——只要按钮在静态背景的Z-order之上(可在资源编辑器调整,或用
SetWindowPos设置),按钮就会在背景之上绘制;
3. 额外优化:固定背景控件层级
如果静态背景作为底层背景,建议把它的Z-order设为最底层,确保所有控件都在它之上绘制,可在窗口初始化时添加:
HWND hStatic = GetDlgItem(hWnd, IDC_STATIC_FOOTER); SetWindowPos(hStatic, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
为什么之前的临时方法有效?
你移除SS_OWNERDRAW后,静态控件采用系统默认绘制逻辑,系统会自动处理控件的层级和重绘顺序,不会覆盖按钮;但自绘时你接管了绘制逻辑,必须严格遵循Windows控件绘制规则,否则就会出现层级混乱问题。
内容的提问来源于stack exchange,提问作者isetsdfse
相关产品推荐
相关产品推荐

