You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 17:15:56