透明控件和无闪烁 MFC 对话框

问题描述 投票:0回答:1

是否可以在基于对话框的 MFC 应用程序中同时拥有:

  1. 透明静态控件
  2. 无闪烁连续变化的背景

到目前为止,我一次只能实现一个目标。对于静态控件透明度,我只需添加一个静态文本控件并覆盖对话框的

OnCtlColor
处理函数,将静态控件的背景模式设置为
TRANSPARENT
,如下所示:

// In the header
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);

// In the source file
BEGIN_MESSAGE_MAP(CMyMFCDialog, CDialogEx)
    ON_WM_CTLCOLOR() // Add this handle
END_MESSAGE_MAP()

HBRUSH CMyMFCDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

    if (nCtlColor == CTLCOLOR_STATIC)
    {
        SetBkMode(*pDC, TRANSPARENT);
        return (HBRUSH)GetStockObject(NULL_BRUSH);
    }

    return hbr;
}

它的工作原理就像一个魅力,我们可以通过在

OnPaint
处理函数中更改对话框的背景颜色来更清楚地看到它:

int r = 255, g = 0, b = 0;

void CMyMFCDialog::OnPaint()
{
    if (IsIconic())
    {
        // ... Unchanged code
    }
    else
    {
        CDialogEx::OnPaint();

        CDC* dc = this->GetDC();
        CRect cr;
        GetClientRect(&cr);
        dc->FillSolidRect(cr, RGB(r, g, b));
    }
}

对于不断变化的背景,我只需非常快速地更改先前填充的背景的 RGB 值即可。为此,我只需添加一个按钮控件,将其 ID 设置为

IDC_BUTTON1
并处理其
OnBnClicked
事件以每 1 毫秒更改一次 RGB 值,如下所示:

// In the header 
afx_msg void OnBnClickedButton1();

// In the source file
BEGIN_MESSAGE_MAP(CMyMFCDialog, CDialogEx)
    ON_BN_CLICKED(IDC_BUTTON1, &CMyMFCDialog::OnBnClickedButton1) // Add this handle
END_MESSAGE_MAP()

void CMyMFCDialog::OnBnClickedButton1()
{
    while (true)
    {
        if (r > 0 && b == 0) {
            r--; g++;
        }
        if (g > 0 && r == 0) {
            g--; b++;
        }
        if (b > 0 && g == 0) {
            r++; b--;
        }

        Invalidate();
        UpdateWindow();
        Sleep(1);
    }
}

正如人们所料,它确实会疯狂地闪烁。我尝试了多种方法(下面有更多详细信息),最有效的是编辑 .rc 文件以将

WS_CLIPCHILDREN
添加到对话框的样式标志中:

IDD_MYMFCDIALOG_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,209,179,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,263,179,50,14
    CTEXT           "TODO: Place dialog controls here.",IDC_STATIC,10,96,300,8
    PUSHBUTTON      "Button1",IDC_BUTTON1,239,46,50,14
END

并重写

OnEraseBkgnd
处理函数以返回
TRUE
FALSE
:

// In the header 
afx_msg BOOL OnEraseBkgnd(CDC* pDC);

// In the source file
BEGIN_MESSAGE_MAP(CMyMFCDialog, CDialogEx)
    ON_WM_ERASEBKGND() // Add this handle
END_MESSAGE_MAP()

BOOL CMyMFCDialog::OnEraseBkgnd(CDC* pDC)
{
    return FALSE;
}

它成功地消除了闪烁效果,但现在我们新制作的透明静态文本控件又恢复了白色背景。我尝试制作一个自定义 CStatic 类来覆盖其

OnPaint
OnCtlColor
,以便将其背景模式设置为
TRANSPARENT
但无济于事,白色背景仍然存在。

我还尝试禁用

WS_CLIPCHILDREN
以使用多种方法实现双缓冲,首先是手动:

void CMyMFCDialog::OnPaint()
{
    if (IsIconic())
    {
        // ... Unchanged code
    }
    else
    {
        CDialogEx::OnPaint();

        CDC* dc = this->GetDC();
        CDC memDC;
        CBitmap bmpDC, *pOldBmp;

        memDC.CreateCompatibleDC(dc);
        CRect cr;
        GetClientRect(&cr);
        bmpDC.CreateCompatibleBitmap(dc, cr.Width(), cr.Height());
        pOldBmp = memDC.SelectObject(&bmpDC);

        memDC.FillSolidRect(cr, RGB(r, g, b));

        dc->BitBlt(cr.left, cr.top, cr.Width(), cr.Height(), &memDC, 0, 0, SRCCOPY);
        memDC.SelectObject(pOldBmp);
    }
}

不幸的是它仍然闪烁,我尝试将双缓冲代码复制到自定义

CStatic
OnPaint
处理函数中,看看它是否会工作得更好,但没有什么改变。

然后我尝试使用一些已经实现的双缓冲,首先使用 Keith Rule 在 2002 年制作的 CMemeDC 标头,并将文件和类重命名为

MyCMemDC
,因为似乎与已经存在的 CMemDC 存在冲突班级:

// In pch.h
#include "mycmemdc.h"

// In the main source file
void CMyMFCDialog::OnPaint()
{
    if (IsIconic())
    {
        // ... Unchanged code
    }
    else
    {
        CDialogEx::OnPaint();

        CDC* dc = this->GetDC();
        MyCMemDC memDC(&dc);

        memDC.FillSolidRect(cr, RGB(r, g, b));
    }
}

闪烁仍然发生,所以我尝试在官方的

CMemDC
类中挖掘,它似乎也是双缓冲实现,所以我也尝试使用它:

// In pch.h
#include "mycmemdc.h"

// In the main source file
void CMyMFCDialog::OnPaint()
{
    if (IsIconic())
    {
        // ... Unchanged code
    }
    else
    {
        CDialogEx::OnPaint();

        CDC* dc = this->GetDC();
        CRect cr;
        GetClientRect(&cr);
        CMemDC memDC(*dc, &cr);
        CDC* pDC = &memDC.GetDC();

        pDC->FillSolidRect(cr, RGB(r, g, b));
    }
}

可悲的是,结果仍然忽隐忽现。

我可能错过了一些东西或者双缓冲实现得不好,上面的代码应该可以轻松重现我的问题,我试图解释几乎每一个步骤,尽管可以通过使用类向导来跳过一些步骤。请随时询问我可能忘记包含的任何详细信息。

c++ user-interface winapi mfc flicker
1个回答
0
投票

我认为您希望有一个消息处理程序来

WM_CTLCOLORSTATIC
并返回与您的绘图函数绘制的背景颜色完全相同的画笔。

https://learn.microsoft.com/en-us/windows/win32/controls/wm-ctlcolorstatic

© www.soinside.com 2019 - 2024. All rights reserved.