带BS_OWNERDRAW标志的按钮显示透明位图问题排查
自绘按钮(BS_OWNERDRAW)透明位图绘制问题
问题现象
- 初始问题:使用
BS_OWNERDRAW标志的按钮显示透明位图时,新图像会与旧图像叠加。 - 添加
FillRect后新问题:按钮按下释放后图像消失;AlphaBlend第三次调用后返回错误码87(参数不正确),调试未发现参数明显异常。
相关代码
初始版本
VOID DrawButton(LPDRAWITEMSTRUCT DrawStruct, HBITMAP* BitmapArray) { HBITMAP CurrentBitmap = BitmapArray[BM_UP]; switch (DrawStruct->itemAction) { case ODA_SELECT: if (DrawStruct->itemState & ODS_SELECTED) { if (DrawStruct->itemState & ODS_FOCUS) CurrentBitmap = BitmapArray[BM_DOWN_FOCUS]; else CurrentBitmap = BitmapArray[BM_DOWN]; } break; case ODA_DRAWENTIRE: if (DrawStruct->itemState & ODS_DISABLED) CurrentBitmap = BitmapArray[BM_DISABLE]; break; case ODA_FOCUS: if (DrawStruct->itemState & ODS_FOCUS) CurrentBitmap = BitmapArray[BM_FOCUS]; break; } HDC CompatibleDC = CreateCompatibleDC(DrawStruct->hDC); HBITMAP hOld = (HBITMAP)SelectObject(CompatibleDC, CurrentBitmap); SelectObject(CompatibleDC, CurrentBitmap); AlphaBlend(DrawStruct->hDC, 0, 0, 50, 24, CompatibleDC, 0, 0, 50, 24, { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }); SelectObject(DrawStruct->hDC, hOld); DeleteDC(CompatibleDC); DeleteObject(CurrentBitmap); }
添加FillRect后的版本
VOID DrawButton(LPDRAWITEMSTRUCT DrawStruct, HBITMAP* BitmapArray) { ... FillRect(DrawStruct->hDC, &DrawStruct->rcItem, CreateSolidBrush(RGB(127, 127, 127))); HDC CompatibleDC = CreateCompatibleDC(DrawStruct->hDC); ... }
演示截图说明
- 按钮状态变化动态演示
- 按钮正常状态截图
- 问题现象截图
问题分析与修复方案
1. 图像叠加问题根源
初始代码未在绘制新位图前清空按钮区域,透明位图的Alpha通道会与原有图像混合,导致叠加效果。
2. FillRect引发的图像消失与资源泄漏
CreateSolidBrush创建的画笔未销毁,会造成GDI资源泄漏;同时硬编码的灰色背景与按钮默认背景不匹配,视觉上会出现“图像消失”的错觉。修复方式:
// 使用系统默认按钮背景色填充,避免视觉违和 HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); FillRect(DrawStruct->hDC, &DrawStruct->rcItem, hBrush); DeleteObject(hBrush); // 必须销毁画笔,防止资源泄漏
3. AlphaBlend错误码87的修复
代码存在三处致命错误:
- 重复调用
SelectObject(CompatibleDC, CurrentBitmap):第一次调用已将位图选入内存DC,第二次调用会打乱GDI状态。 - 错误地将旧位图选入目标DC(
DrawStruct->hDC):应该将旧位图选回内存DC,还原其初始状态。 - 错误删除传入的位图:
CurrentBitmap来自外部数组,不属于当前函数创建的资源,删除会导致后续使用失败,甚至触发GDI错误。
修复后的核心代码:
HDC CompatibleDC = CreateCompatibleDC(DrawStruct->hDC); HBITMAP hOld = (HBITMAP)SelectObject(CompatibleDC, CurrentBitmap); // 移除重复的SelectObject调用 // 使用按钮实际尺寸,避免硬编码错位 int btnWidth = DrawStruct->rcItem.right - DrawStruct->rcItem.left; int btnHeight = DrawStruct->rcItem.bottom - DrawStruct->rcItem.top; AlphaBlend(DrawStruct->hDC, 0, 0, btnWidth, btnHeight, CompatibleDC, 0, 0, 50, 24, { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }); // 将旧位图选回内存DC,而非目标DC SelectObject(CompatibleDC, hOld); DeleteDC(CompatibleDC); // 移除DeleteObject(CurrentBitmap); 该行代码
4. 焦点状态处理优化
原代码未处理失去焦点的情况,导致焦点状态无法清除,修复如下:
case ODA_FOCUS: if (DrawStruct->itemState & ODS_FOCUS) CurrentBitmap = BitmapArray[BM_FOCUS]; else CurrentBitmap = BitmapArray[BM_UP]; // 失去焦点时恢复正常状态 break;
内容的提问来源于stack exchange,提问作者MyDoomH
相关产品推荐
相关产品推荐

