从 CDC 创建 CBitmap?

问题描述

使用 C++/MFC 和 GDI(而不是 GDI+),总体目标是创建一个带图案的 HBrush,它将在 OnCtlColor 中用红色勾勒出一个编辑控件,并能够打开和关闭轮廓。为此,您可以使用 HBrush 将位图附加到 CreatePatternBrush。这是使用存储的位图资源执行此操作的代码

CDialog::OnInitDialog();
BOOL ok = redBoxBitmap.LoadBitmap(MAKEINTRESOURCE(IDB_mespe_EditBox_Red));
ok = redBoxBrush.CreatePatternBrush(&redBoxBitmap);

并在OnCtlColor

HBrush CModelEditorSpecies::OnCtlColor(CDC* pDC,CWnd* pWnd,UINT nCtlColor)
{
    HBrush hbr;
    int ctrlID=pWnd->GetDlgCtrlID();
    if(ctrlID==IDC_MyEditControl)
        hbr=(HBrush) redBoxBrush;
    else
        hbr = CDialog::OnCtlColor(pDC,pWnd,nCtlColor);

    return hbr;
}

以上代码都可以正常工作。但是,这取决于为编辑控件调整大小的位图。我现在需要的是能够在 C++ 程序中创建位图,其大小与控件的客户区一致,这取决于控件的设计大小(在对话框编辑器中)和用户对文本大小的设置Windows 10 设置。

我找不到一种直接的方法来构建位图,或者更好的是,创建一个适当大小的空位图(可以),将其选入 CDC(可以),绘制红色框进入它(可以做),然后从 CDC提取更新位图(怎么做?)。

任何人都可以建议如何以编程方式创建位图,或者建议一种更好的方法来在程序调用时将编辑控件框为红色?

为回应@Constantine GeorgIoU 3/9 的回答而添加

代码

CBitmap redBoxBitmap; // member variables of class CModelEditorSpecies
CBrush redBoxBrush;

BOOL CModelEditorSpecies::OnInitDialog()
{
    CDialog::OnInitDialog();
    BOOL ok;
    CRect r; defaultSpecies1Ctrl.GetClientRect(&r);
    xx(r.Width(),r.Height()/*,redBoxBrush*/);
    ok = redBoxBrush.CreatePatternBrush(&redBoxBitmap);
    //...
}

void CModelEditorSpecies::xx(const int w,const int h)
{
    CDC *pDC=GetDC();
    redBoxBitmap.CreateCompatibleBitmap(pDC,w,h);
    
    // Create a red pen
    CPen redPen;
    redPen.CreatePen(PS_SOLID,1,RGB(255,0));
    
    // Draw the bitmap - red pen & default background brush
    CBitmap *pOldBitmap=pDC->SelectObject(&redBoxBitmap);
    pDC->SelectObject(&redPen);
    CBrush editBoxBrush;
    editBoxBrush.CreateSysColorBrush(COLOR_WINDOW);
    pDC->SelectObject(&editBoxBrush);
    pDC->Rectangle(0,h);
        
    pDC->SelectObject(pOldBitmap);
    
    // Create the edit-control custom brush
    redBoxBrush.CreatePatternBrush(&redBoxBitmap);
    return;
}

代码生成一个全黑的编辑控件,就好像所使用的位图是单色的一样。如果在 dc 中绘制不影响位图,或者如果在 dc 兼容位图中绘制不使用 redPeneditBoxBrush 中的颜色,如@IInspectable 所建议的那样,这将是预期的。

解决方法

这是创建画笔的方法 - 使用了 Win32 函数而不是它们的 MFC 包装器,但您可以弄清楚。

BOOL CModelEditorSpecies::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Get edit-control's size
    RECT rc;
    GetDlgItem(IDC_MyEditControl)->GetClientRect(&rc);

    // Create the bitmap and a memory-DC
    HDC hDC = ::GetDC(HWND_DESKTOP);
    HDC mDC = CreateCompatibleDC(hDC);
    HBITMAP hBmp = CreateCompatibleBitmap(hDC,rc.right,rc.bottom);
    ::ReleaseDC(HWND_DESKTOP,hDC);

    // Create a red pen
    HPEN hPen = CreatePen(PS_SOLID,1,RGB(255,0));

    // Draw the bitmap - red pen & default background brush
    HBITMAP hOldBmp = (HBITMAP) SelectObject(mDC,hBmp);
    HPEN hOldPen = (HPEN) SelectObject(mDC,hPen);
    HBRUSH hOldBr = (HBRUSH) SelectObject(mDC,GetSysColorBrush(COLOR_WINDOW));
    Rectangle(mDC,rc.left,rc.top,rc.bottom);
    SelectObject(mDC,hOldBr);
    SelectObject(mDC,hOldPen);
    SelectObject(mDC,hOldBmp);

    // Create the edit-control custom brush
    redBoxBrush = CreatePatternBrush(hBmp);

    // Clean-up - the SysColorBrush doesn't need to be deleted
    DeleteObject(hPen);
    DeleteObject(hBmp);
    DeleteDC(mDC);

    return TRUE;
}

HBRUSH CModelEditorSpecies::OnCtlColor(CDC* pDC,CWnd* pWnd,UINT nCtlColor)
{
    return (nCtlColor == CTLCOLOR_EDIT && pWnd->GetDlgCtrlID() == IDC_MyEditControl) ?
        redBoxBrush : CDialogEx::OnCtlColor(pDC,pWnd,nCtlColor);
}

还要检查我在评论中建议的替代方案。这将绘制到编辑控件的客户区。


编辑:

我上面发布的代码确实有效,在它运行后,剩下的就是 HBRUSH 句柄(编辑框的模式画笔)。我不明白你为什么坚持使用 MFC 包装器。它们是“更高级别”的瘦包装器,所以......实际上是“瘦”的,您仍然必须执行几乎完全相同的操作(创建、选择到 DC、执行某些绘图操作、从 DC 中选择) GDI的。您唯一不需要执行的操作是删除资源(对象的析构函数会为您调用 DeleteObject())。

无论如何,如果您更喜欢 MFC 而不是 GDI,让我们看看您的代码有什么问题。它有很多问题:

  • 首先,您需要一个 memory DC 在位图上绘制,您通过调用 GetDC() 获得的窗口 DC 绘制在窗口的表面上。
  • 调用GetDC()得到的DC必须返回给系统(ReleaseDC()),因为pDC只是一个指针,编译器不会调用析构函数。
  • 您选择到 DC 中的对象必须在销毁之前被选中,否则可能会发生内存泄漏。

所以你的代码应该修改如下:

void CModelEditorSpecies::xx()
{
    CRect r;
    GetDlgItem(IDC_MyEditControl)->GetClientRect(&r);
    
    // Create the bitmap and a memory-DC
    CBitmap redBoxBitmap;
    CDC mDC,*pDC = GetDC();
    redBoxBitmap.CreateCompatibleBitmap(pDC,r.Width(),r.Height());
    mDC.CreateCompatibleDC(pDC);
    ReleaseDC(pDC);

    // Create a red pen and get the default background brush
    CPen redPen;
    redPen.CreatePen(PS_SOLID,0));
    CBrush editBoxBrush;
    editBoxBrush.CreateSysColorBrush(COLOR_WINDOW);

    // Draw the bitmap - red pen & default background brush
    CBitmap *pOldBitmap = mDC.SelectObject(&redBoxBitmap);
    CPen *pOldPen = mDC.SelectObject(&redPen);
    CBrush *pOldBrush =mDC.SelectObject(&editBoxBrush);
    mDC.Rectangle(r);
    mDC.SelectObject(pOldBrush);
    mDC.SelectObject(pOldPen);
    mDC.SelectObject(pOldBitmap);

    // Create the edit-control custom brush
    redBoxBrush.CreatePatternBrush(&redBoxBitmap);
}

与GDI版本基本一致。