将静态控件子类化到对话框窗口

Posted

技术标签:

【中文标题】将静态控件子类化到对话框窗口【英文标题】:Subclassing a static control to the dialog window 【发布时间】:2015-07-01 08:53:00 【问题描述】:

让我从目标开始:我要将静态控件的背景设置为对话框,而静态控件的内容不是固定字符串,并且在 switch case 语句之后会发生变化。换句话说,我打算将具有动态内容的静态控件的背景设置为具有静态双图图像的对话框窗口。

为了达到这个目标,三个句柄消息定义如下: OnEraseBkgnd(), OnDestroy(), OnCtlColor()

为了显示文本,在每个 Switch case 语句之后,我设置了一个 SetWindowText 函数:

::SetWindowText(GetDlgItem(IDC_STATIC_FORM)->m_hWnd, ArrayName);

这里唯一的问题是overlapping。预览事件或案例的所有文本仍保留在SetWindowText。为了在每个SetWindowText 定义一个 InvalidateRect(Null) 之后解决这个问题并且这个问题也解决了,但是在这个过程中我收到了一个总是闪烁的对话框,如果我从技术上说,闪烁。我认为问题只是因为 Invalidate 应用于整个对话框而不仅仅是静态控制。 OnCtlColor 被覆盖对话框而不是控件。因为我只是想让控件失效,所以我必须继承 CStatic 控件,只覆盖它的 OnCtlColor(不是对话框的),并且只调用它的 Invalidate。

我的问题:

假设我在主代码中声明了一个用于静态控制的变量并在构造函数中对其进行了初始化,我使用基类 CDialogEX 定义了一个名为 CSTATICCTRL 的类,那么消息处理程序如下:

变量成员定义为:

CSTATICCTRL m_STATIC_FORM;

BOOL CSTATICCTRL::OnEraseBkgnd(CDC* pDC)

    // TODO: Add your message handler code here and/or call default
    CDC dcMemory;
    dcMemory.CreateCompatibleDC(pDC);
    CBitmap* pOldbitmap = dcMemory.SelectObject(&Background);
    CRect rcClient;

    const CSize& sbitmap = bitmapSize;
    pDC->BitBlt(0, 0, sbitmap.cx, sbitmap.cy, &dcMemory, 0, 0, SRCCOPY);
    /*The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels
    from the specified source device context into a destination device context.*/
    dcMemory.SelectObject(pOldbitmap);

    return 1;
    //return CDialogEx::OnEraseBkgnd(pDC);


HBRUSH CSTATICCTRL::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)

    //HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

    if (pWnd->GetDlgCtrlID() == IDC_STATIC_FORM)
    
        pDC->SetBkMode(TRANSPARENT);
        pDC->SetTextColor(RGB(255, 255, 255));
        Invalidate();

    
    return (HBRUSH)GetStockObject(NULL_BRUSH);


void CSTATICCTRL::OnDestroy()  //free the resources created for the bitmap,

    CDialog::OnDestroy();
    Background.DeleteObject(); // Delete Background bitmap
    BrushHol.DeleteObject();
    // Delete Brush

我也在主代码上留下了这些信息。然后我尝试以这种方式在每个 switch case 之后使静态控件无效:

 m_STATIC_FORM.Invalidate();

此外,在新类的末尾添加了一个 PreSubclassWindow() 句柄,如下所示。

void CSTATICCTRL::PreSubclassWindow()

    // TODO: Add your specialized code here and/or call the base class

    CDialogEx::PreSubclassWindow();
    ModifyStyle(0, BS_OWNERDRAW);   // make the button owner drawn

 

什么也没发生!我不知道我的代码有什么问题!!采用这种新方式,静态控件内容发生了变化,但不透明,重叠仍然存在。

更新:

其实刚才我在注释了几行之后才意识到新的Class对静态控件和对话框没有任何影响!!我认为首先我应该激活 CSTATICCTRL 然后我可以处理它的句柄消息。

对于对话代码,使用了这三个句柄消息:

BOOL CMainDlg::OnEraseBkgnd(CDC* pDC)

    // TODO: Add your message handler code here and/or call default
    //************************************************************************************
    CDC dcMemory;
    dcMemory.CreateCompatibleDC(pDC);
    CBitmap* pOldbitmap = dcMemory.SelectObject(&Background);
    CRect rcClient;
    GetClientRect(&rcClient);
    const CSize& sbitmap = bitmapSize;
    pDC->BitBlt(0, 0, sbitmap.cx, sbitmap.cy, &dcMemory, 0, 0, SRCCOPY);
    dcMemory.SelectObject(pOldbitmap);
    return TRUE;
    //return CDialog::OnEraseBkgnd(pDC);





void CMainDlg::OnDestroy()  //free the resources created for the bitmap,

    CDialog::OnDestroy();

    // Delete Brush


HBRUSH CMainDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)


    // TODO:  Change any attributes of the DC here

    return (HBRUSH)GetStockObject(NULL_BRUSH);

【问题讨论】:

您对 Windows API 的了解是否足以理解,MFC 与它有何关系?听起来您几乎迷失了方向,试图同时学习两种 API,其中一种是另一种的强制性先决条件。 还没有!!!!不过我正在学习。好的,谢谢。 @IInspectable 你可以同时学习两者,我就是这样做的。但是你需要注意界限。还有一些我不太了解的东西,比如父窗口和子窗口之间的消息路由,因为 MFC 对你隐藏了一些。 @Mark:正如您所指出的,当您尝试同时学习两者时,您将无法同时流利地学习两者。我宁愿不提倡浪费时间,因为回报太少。此外,MFC 不会隐藏任何东西。如果您需要了解消息路由,可以阅读 TN 文章,如果需要,还可以阅读源代码。 【参考方案1】:

我还没有尝试过,但我认为它会起作用。

在您的对话框中覆盖 OnCtlColor。当它检测到正在绘制静态控件时,返回一个空画笔。这样可以防止背景被擦除,这就是导致闪烁的原因。

在静态控件上设置WS_EX_TRANSPARENT 样式。这将导致在绘制控件之前重新绘制控件下方的对话框部分,从而擦除旧文本。

编辑:根据an article by Raymond Chen WS_EX_TRANSPARENT 样式在这种情况下没有帮助,因为对话框不是静态控件的兄弟,它是父控件。您可以使用RedrawWindow 重新绘制静态控件下方的部分。

您不需要自己的课程,CStatic 可以很好地处理这些修改。如果你确实决定你需要自己的类,你不应该从CDialogEx派生它,它应该保留给对话框窗口。

pStatic->SetWindowText(str);
CRect rcChild;
pStatic->GetClientRect(&rcChild);
pStatic->MapWindowPoints(this, &rcChild);
RedrawWindow(rcChild, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
pStatic->InvalidateRect(NULL);

【讨论】:

我在问题的 UPDATE 部分中包含了对话框代码的句柄消息。此外,对于静态控件的透明度,正如您在第一个代码中看到的那样,我编写了一行代码来执行此操作SetBkMode(TRANSPARENT);。为了更加确定,我将资源视图-> 对话框属性-> 静态控件属性-> Transparet== FALSE 修改为 TRUE。我认为问题在于我定义的 CSTATICCTRL 类对主代码没有任何影响,因此当我调用 m_STATIC_FORM.Invalidate() 时,静态控件内容保持不变。 不考虑新类,并且在覆盖OnCtlColor 之后,唯一的问题是来自不同事件的静态控制文本重叠。出于这个原因,我决定使用 Invalidate() 函数,这会导致整个对话框闪烁,而不仅仅是静态控件(以防止重叠)。 @Braian 您可以在与您正在更新的控件重叠的所有控件上调用RedrawWindow 我将您编写的代码添加到我调用静态控件的每个 Switch case 语句中。我刚刚将 pStatic 定义为 CWnd* 类。我收到此错误:没有重载函数“CWnd::MapWindowPoints”的实例与参数列表参数类型匹配:(CMainDlg *, CRect *, int) 对象类型为:CWnd @Braian 抱歉,我将 MFC 调用与 API 调用混淆了,我有一个额外的参数。我已经修复了这个例子。

以上是关于将静态控件子类化到对话框窗口的主要内容,如果未能解决你的问题,请参考以下文章

将 WriteableBitmap 线程化到图像控件

MFC中动态添加控件----寻找多年的秘籍,吐血推荐

怎么样在MFC中创建动态控件

win32day09-对话框/子控件/静态块/按钮/文本编辑框

MFC如何将窗口中所有控件大小随父窗口大小改变

VC中改变窗口背景颜色和控件背景颜色