c++ 在MFC程序中,当对话框中的所有控件都绘制出来时,会发送哪些消息?

wnavrhmk  于 2023-06-25  发布在  其他
关注(0)|答案(1)|浏览(110)

我创建了一个从CDialogEx派生的对话框类,在对话框界面上有一个方形的CStatic控件。我希望控件最初显示为黑色背景,并在其上绘制一条绿色对角线。
我重写了下面两个基类的函数,在OnCtlColor中设置背景色,在OnWindowPosChanged中绘制对角线。
运行程序后,控件的背景色成功变为黑色,绿色对角线也绘制成功。但是,很明显,对角线在黑色背景之下,甚至在控件之下(或者控件的绘制覆盖了对角线的绘制,因为我试图注解OnCtlColor函数,而对角线仍然被控件阻挡)。
我在两个函数中都设置了断点,发现OnWindowPosChanged中的断点在OnCtlColor中的断点之前触发。所以我猜对角线被控件覆盖的原因是因为OnWindowPosChanged在OnCtlColor之前执行。
所以我想问一下,如果我想在对话框中的某个控件绘制完成(或者整个对话框绘制完成)之后做一些事情,代码应该写在哪里?

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

    HBRUSH hbr = __super::OnCtlColor(pDC, pWnd, nCtlColor);
    if (pWnd->GetDlgCtrlID() == IDC_CANVAS_PREVIEW)
    {
        hbr = CreateSolidBrush(RGB(0, 0, 0));
        return hbr;
    }

    // TODO:  Return a different brush if the default is not desired
    return hbr;
}

void CreateCutOffDitchDlg::OnWindowPosChanged(WINDOWPOS* wndpos)//test
{

    __super::OnWindowPosChanged(wndpos);
    if (wndpos->flags & SWP_SHOWWINDOW) {
        CRect r;
        canvasPreview->GetWindowRect(r);
        CDC* pDC = canvasPreview->GetDC();
        CPen pen(PS_SOLID, 2, RGB(0, 255, 0));
        pDC->SelectObject(&pen);
        pDC->MoveTo(0, 0);
        pDC->LineTo(r.Width(), r.Height());
    }
}

在上面的代码中,“canvasPreview”是CStatic控件,“IDC_CANVAS_PREVIEW”是对应的ID。
运行后,控件显示如下:

如果Map消息的“ON_WM_CTLCOLOR()”宏被注解掉,对角线仍然被遮挡::

velaa5lx

velaa5lx1#

WM_CTLCOLOR消息(OnCtlColor())用于某些控件使用不同的前景色或背景色(使用其他“标准”绘制)的情况。在控件上绘制对角线等功能是自定义绘制。当然,只在窗口位置改变(在本例中变为可见)时才这样做是不正确的。你的应用程序应该处理正常绘制的情况,也就是当窗口得到客户区或它的一部分“无效”(作为隐藏/取消隐藏,移动另一个窗口在它前面,例如对话框,然后关闭它,移动父窗口经过可见的桌面区域,然后移动它,等等,或编程使它无效)。如果一个窗口包含一个无效区域,它将在消息队列即将变空之前收到一个WM_PAINT消息。因此,您必须为WM_PAINT消息编写一个处理程序。你可能想看看Painting and Drawing
在MFC中有三种方法可以做到这一点:
1.使IDC_CANVAS_PREVIEW控件由所有者绘制。父窗口(对话框)将获得对OnDrawItem()成员(WM_DRAWITEM)的调用,您必须覆盖该成员以执行自定义绘制。使用DRAWITEMSTRUCT结构的HDC成员绘制控件。
1.定义一个CStatic派生类并重写DrawItem()函数,以执行自定义绘制。再次使用DRAWITEMSTRUCT结构的HDC成员绘制控件。使用VS的ClassWizzard将IDC_CANVAS_PREVIEW与新定义类的示例关联起来(添加一个控制变量)。这将控件“子类化”。
1.或者,您可以重写新类中的OnPaint()处理程序(WM_PAINT)。使用CDC*参数绘制控件。它为控件的客户端区域 Package 了一个HDC,由MFC框架获得,调用BeginPaint()函数(不要自己调用BeginPaint()/EndPaint())。在这种情况下,控件不需要是所有者描述的。
第一个方法是最容易实现的,因为您所要做的就是处理父对话框类的WM_DRAWITEM消息(Map到OnDrawItem()成员),使用资源编辑器(Properties-> Messages)很容易完成。另外两个需要定义一个新的C类,但是这个代码是可重用的(在另一个对话框或项目中)。
最后,关于你的代码,在GDI中,你通常必须销毁或释放你创建的对象,否则会导致泄漏。对于每个create/get/select调用,必须有一个对应的destroy/release/select。例如,对于GetDC()调用,您应该在完成后调用ReleaseDC()。并且在返回之前,选择到DC中的笔应该已经从DC中选择出来了--不需要显式地销毁创建的笔,因为CPen析构函数会为您完成这一操作(但这是MFC)。根据WM_CTLCOLOR文档,您在OnCtlColor()中创建的画笔也应该被销毁。但是由于画笔必须返回,因此不能在函数内部执行此操作;最好的方法是缓存画笔句柄,即在对话框的构造器中创建它,在每个OnCtlColor()调用中使用它(而不是创建它),并在对话框的析构器中销毁它-如果您在对话框中将其定义为CBrush对象(不是CBrush*HBRUSH),这是不必要的,因为C
会在对话框对象被销毁时调用它的析构函数。或者,它可以全局创建,永远不会被销毁,即在整个应用程序的生命周期内(毕竟它是一个单一的对象)-windows资源管理器将在应用程序退出时自动销毁它。

相关问题