【问题标题】:Ribbon button items with large images and checkboxes带有大图像和复选框的功能区按钮项目
【发布时间】:2014-03-10 13:55:29
【问题描述】:

我有一个附加到这样的拆分功能区按钮的菜单(VS2008,功能包):

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

我想在每个菜单项前面放置复选框,并在 CN_UPDATE_COMMAND_UI 处理程序中提供了 SetCheck() 调用,但只有在我禁用大图标时才会显示复选框。

有没有办法在CMFCRibbonButton 菜单中使用复选框和大图标?如果不是,最好的解决方法是什么?

【问题讨论】:

    标签: mfc mfc-feature-pack


    【解决方案1】:

    我找到了一种可用的解决方法,通过在图标上混合带有复选标记的绿色圆圈进行 alpha 混合来检查大图标——与 Windows 用于在控制面板中标记默认音频设备或打印机的解决方案相同:

    这是我的 CMFCRibbonButtonEx 类声明:

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

    IDB_BIG_ICON_CHECKMARK 是一个带有 alpha 通道的 32 位 bmp:Download here

    遗憾的是,在 OnCmdMsg 中仅维护由功能区框架创建的假 CMFCRibbonButton,因此 SetCheck()DrawImage() 中的 IsChecked() 调用没有任何影响。您只需要在菜单的子项中找到真正的CMFCRibbonButtonEx,如下所示:

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

    如果有人知道如何将CCmdUI维护的假CMFCRibbonButton(或CMFCRibbonBaseElement)连接到最初创建的CMFCRibbonButtonEx,请给我留言。

    【讨论】:

      【解决方案2】:

      "每个菜单项前面的复选框"

      下面的图片是你要找的吗?如果是,这可以通过编辑功能区 xml 文件来实现。

      【讨论】:

      • 不,我想要 same 子项的复选框和图标。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-12
      • 2016-02-03
      • 2015-12-29
      • 2019-04-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多