MFC Ribbon:更改按钮的图标(动态)

MFC Ribbon: change icon of a button (dynamically)

对于CMFCRibbonBar,我想根据应用程序状态更改按钮的图标。

(我已经研究了一段时间的代码和文档,但没有发现任何有用的东西。)

我从 CMFCRibbonButton 派生了一个 class CMFCRibbonButtonEx 来控制图标的绘制方式。在这种特殊情况下,我想在原始图标上 alpha-blend 一个绿色复选标记:

Header:

class CMFCRibbonButtonEx : public CMFCRibbonButton
{
// Construction
public:
    CMFCRibbonButtonEx();
    CMFCRibbonButtonEx(UINT nID, LPCTSTR lpszText, int nSmallImageIndex = -1, int nLargeImageIndex = -1, BOOL bAlwaysShowDescription = FALSE);
    CMFCRibbonButtonEx(UINT nID, LPCTSTR lpszText, HICON hIcon, BOOL bAlwaysShowDescription = FALSE, HICON hIconSmall = NULL, BOOL bAutoDestroyIcon = FALSE, BOOL bAlphaBlendIcon = FALSE);

// Overridden
    void SetCheck(BOOL bCheck = TRUE);
    void DrawImage(CDC* pDC, RibbonImageType type, CRect rectImage);

// Attributes
private:
    BOOL m_bChecked;

// Helper
private:
    void DrawCheckmark(CDC* pDC, int CheckmarkResourceBitmapID, RECT *r);
    void PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp);
};  

实施:

CMFCRibbonButtonEx::CMFCRibbonButtonEx() : CMFCRibbonButton() { }
CMFCRibbonButtonEx::CMFCRibbonButtonEx(UINT nID, LPCTSTR lpszText, int nSmallImageIndex, int nLargeImageIndex, BOOL bAlwaysShowDescription)
    : CMFCRibbonButton(nID, lpszText, nSmallImageIndex, nLargeImageIndex, bAlwaysShowDescription) { }
CMFCRibbonButtonEx::CMFCRibbonButtonEx(UINT nID, LPCTSTR lpszText, HICON hIcon, BOOL bAlwaysShowDescription, HICON hIconSmall, BOOL bAutoDestroyIcon, BOOL bAlphaBlendIcon)
    : CMFCRibbonButton(nID, lpszText, hIcon, bAlwaysShowDescription , hIconSmall, bAutoDestroyIcon, bAlphaBlendIcon) { }
void CMFCRibbonButtonEx::SetCheck(BOOL bCheck)
{
    m_bChecked = bCheck;
}
void CMFCRibbonButtonEx::DrawImage(CDC* pDC, RibbonImageType type, CRect rectImage)
{
    CMFCRibbonButton::DrawImage(pDC, type, rectImage);
    if (type == RibbonImageLarge && m_bChecked) 
        DrawCheckmark(pDC, IDB_BIG_ICON_CHECKMARK, &rectImage);
}
void CMFCRibbonButtonEx::DrawCheckmark(CDC* pDC, int CheckmarkResourceBitmapID, RECT *r)
{
    HDC  hdc;
    CDC  *dc;
    CDC dcMem;
    CBitmap cbm;

    VERIFY(hdc = pDC->m_hDC);
    VERIFY(dc = pDC);

    dcMem.CreateCompatibleDC(dc);

    cbm.LoadBitmap(CheckmarkResourceBitmapID);
    PremultiplyBitmapAlpha(dcMem.m_hDC, cbm);
    SelectObject(dcMem.m_hDC, cbm.m_hObject);

    BLENDFUNCTION bf;
    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.SourceConstantAlpha = 255;
    bf.AlphaFormat = AC_SRC_ALPHA;
    ::AlphaBlend(hdc, r->left, r->top, r->right-r->left, r->bottom-r->top, dcMem, 0, 0, 32, 32, bf);

    VERIFY(dcMem.DeleteDC());
}
void CMFCRibbonButtonEx::PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp)
{
   BITMAP bm = { 0 };
   GetObject(hBmp, sizeof(bm), &bm);
   BITMAPINFO* bmi = (BITMAPINFO*) _alloca(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
   ::ZeroMemory(bmi, sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
   bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
   BOOL bRes = ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, NULL, bmi, DIB_RGB_COLORS);
   if( !bRes || bmi->bmiHeader.biBitCount != 32 ) return;
   LPBYTE pBitData = (LPBYTE) ::LocalAlloc(LPTR, bm.bmWidth * bm.bmHeight * sizeof(DWORD));
   if( pBitData == NULL ) return;
   LPBYTE pData = pBitData;
   ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, pData, bmi, DIB_RGB_COLORS);
   for( int y = 0; y < bm.bmHeight; y++ ) {
      for( int x = 0; x < bm.bmWidth; x++ ) {
         pData[0] = (BYTE)((DWORD)pData[0] * pData[3] / 255);
         pData[1] = (BYTE)((DWORD)pData[1] * pData[3] / 255);
         pData[2] = (BYTE)((DWORD)pData[2] * pData[3] / 255);
         pData += 4;
      }
   }
   ::SetDIBits(hDC, hBmp, 0, bm.bmHeight, pBitData, bmi, DIB_RGB_COLORS);
   ::LocalFree(pBitData);
}

这就是我使用 CMFCRibbonButtonEx 项构建 'Filter' 子菜单的方式:

m_pBtnFilter->RemoveAllSubItems();

int i;
for (i = 0; i < m_csaNames.GetSize(); i++)
{
    std::auto_ptr<CMFCRibbonButtonEx> apBtn(new CMFCRibbonButtonEx(ID_FILTER_BASE + i, (LPCTSTR)m_csaNames[i], m_tbiIcons.ExtractIcon(m_nIcons[i], true, NULL, true, true));
    apBtn->SetToolTipText(m_csaTooltips[i]);
    m_pBtnFilter->AddSubItem(apBtn.release());
}

我相信您可以毫无问题地将其调整到主菜单按钮(以及您希望应用程序状态对图标产生影响的特殊方式)。