Ribbon button items with large images and checkboxes

╄→гoц情女王★ 提交于 2019-12-24 12:35:13

问题


I've got a menu attached to a split ribbon button like this (VS2008, Feature Pack):

std::auto_ptr<CMFCRibbonButton> apBtn3(new CMFCRibbonButton(ID_RIBBON_BTN_3, _T("Split Button"), 2, 2));
apBtn3->SetMenu(IDR_RIBBON_MENU_1, TRUE);
apBtn3->SetAlwaysLargeImage();
apBtn3->RemoveSubItem(0);
std::auto_ptr<CMFCRibbonButton> apSubButton(new CMFCRibbonButton(ID_RIBBON_MBTN_1, _T("Item 1"), 2, 2));   
apSubButton->SetAlwaysLargeImage();
apBtn3->AddSubItem(apSubButton.release(), 0);
pPanel1->Add(apBtn3.release());

I want to put checkboxes in front of each menu item and have provided SetCheck() calls in the CN_UPDATE_COMMAND_UI handler but checkboxes will only show up, if I disable the large icons.

Is there any way to use checkboxes along with large icons in CMFCRibbonButton menus? If not, what would be the best possible workaround?


回答1:


I found a usable workaround to check large icons by alpha-blending a green circle with a checkmark in it over the icon -- the same solution Windows uses to mark the default audio device or printer in the control panel:

This is my CMFCRibbonButtonEx class declaration:

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);
};

Here are the class function definitions:

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);
}

IDB_BIG_ICON_CHECKMARK is a 32bit bmp with alpha channel:

Download here

Sadly, in OnCmdMsg only a fake CMFCRibbonButton created by the ribbon framework is being maintained, so SetCheck() doesn't have any effect on an IsChecked() call in DrawImage(). You just have to find the real CMFCRibbonButtonEx in the subitems of the menu like this:

BOOL CEasyCashView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) 
{
    ...

    else if (nID >= ID_MY_ITEMS_BASE && nID < ID_ITEMS_BASE+MAX_MY_ITEMS)
    {
        if (nCode == CN_COMMAND)
        {
            OnMyItemsCommand(nID);      
        }
        else if (nCode == CN_UPDATE_COMMAND_UI)
        {

            ((CCmdUI*)pExtra)->SetCheck(myItemsCheckedArray[nID-ID_MY_ITEMS_BASE] == TRUE); 
            // this won't have any effect, use code below

            CMFCRibbonButton* pMyMainMenuButton;
            if (pMyMainMenuButton = ((CMainFrame*)AfxGetMainWnd())->m_pMyMainMenuButton)
            {
                int i;
                for (i = 0; i < pMyMainMenuButton->GetSubItems().GetCount(); i++)
                    if (pMyMainMenuButton->GetSubItems()[i]->GetID() == nID)
                    {
                        ((CMFCRibbonButtonEx*)pMyMainMenuButton->GetSubItems()[i])->SetCheck(myItemsCheckedArray[nID-ID_MY_ITEMS_BASE] == TRUE);
                        break;
                    }
            }

            return TRUE;
        }
    }

Should anyone know how to connect the fake CMFCRibbonButton (or CMFCRibbonBaseElement) maintained by CCmdUI to the originally created CMFCRibbonButtonEx, please leave me a comment.




回答2:


"checkboxes in front of each menu item"

Is the below picture what you're looking for? If yes, this can be achieved by editing the ribbon xml file.



来源:https://stackoverflow.com/questions/22302164/ribbon-button-items-with-large-images-and-checkboxes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!