是否可以在基于对话框的 MFC 应用程序中同时拥有:
到目前为止,我一次只能实现一个目标。对于静态控件透明度,我只需添加一个静态文本控件并覆盖对话框的
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));
}
}
可悲的是,结果仍然忽隐忽现。
我可能错过了一些东西或者双缓冲实现得不好,上面的代码应该可以轻松重现我的问题,我试图解释几乎每一个步骤,尽管可以通过使用类向导来跳过一些步骤。请随时询问我可能忘记包含的任何详细信息。
我认为您希望有一个消息处理程序来
WM_CTLCOLORSTATIC
并返回与您的绘图函数绘制的背景颜色完全相同的画笔。
https://learn.microsoft.com/en-us/windows/win32/controls/wm-ctlcolorstatic