为什么对话框会在不刷新的情况下删除其内容

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么对话框会在不刷新的情况下删除其内容相关的知识,希望对你有一定的参考价值。

我有一个非常奇怪的问题,一个窗口似乎正在擦除它的内容,而不是在擦除它之后重绘它。这个对话框来自CDhtmlDialog,我认为这是问题的一部分。存在某种非确定性的代码执行,导致某些代码在某些情况下先于其他代码执行,而在其他情况下则相反。

涉及的消息处理程序是:

BEGIN_MESSAGE_MAP(CCalcDrillDownDlg, CDHtmlDialog)
    ON_WM_PAINT()
END_MESSAGE_MAP()

BEGIN_EVENTSINK_MAP(CCalcDrillDownDlg, CDHtmlDialog)
     ON_EVENT(CCalcDrillDownDlg, AFX_IDC_BROWSER, 250 /* BeforeNavigate2 */, _OnBeforeNavigate2b, VTS_DISPATCH VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PBOOL)
END_EVENTSINK_MAP()

OnInitDialog()功能如下:

BOOL CCalcDrillDownDlg::OnInitDialog()
{
    SetHostFlags(DOCHOSTUIFLAG_FLAT_SCROLLBAR);

    CDHtmlDialog::OnInitDialog(); // << will eventually call _OnBeforeNavigate2b()

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    LoadFromResource(IDR_CALC_DRILLDOWN); // << will eventually call _OnBeforeNavigate2b()
    CString title = getStr2Ptr(22574);
    SetWindowText(title);
    ShowWindow(SW_SHOW);

    return TRUE;  // return TRUE  unless you set the focus to a control
}

这是OnPaint()函数:

void CCalcDrillDownDlg::OnPaint() 
{
    if (IsIconic())
    {
        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

        // Center icon in client rectangle
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDHtmlDialog::OnPaint();
    }
}

我没有把_OnBeforeNavigate2b()函数的内容,因为它似乎与重绘系统没有任何关系。

所以似乎发生的事情是,有时候,在调用CCalcDrillDownDlg::OnPaint()之前,会以某种方式绘制对话框内容。如果发生这种情况,那么调用CDHtmlDialog::OnPaint()将擦除窗口内的内容。

其他时候,在调用CCalcDrillDownDlg::OnPaint()之前,内容不会在窗口上绘制。如果发生这种情况,那么对CDHtmlDialog::OnPaint()的调用可能仍会擦除窗口上尚未绘制的内容,然后在调用CCalcDrillDownDlg::OnPaint()之后的某个时间重新绘制。

当系统正确地重绘窗口时,Spy ++不捕获任何消息,因此我删除了从这个问题生成的消息。

有没有人知道重绘是如何完成的以及为什么订单有时会变得有效?

Edit

以下是IDR_CALC_DRILLDOWN资源的内容:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Calculation Drilldown</title>
    <style type="text/css">
        body { overflow-y: auto; font-family: arial, Helvetica, sans-serif; font-size: 90%; }

        a:link { color: black; }
        a:visited { color: black; }
        table { border-collapse: collapse; }

        tr.runcache td { background-color: #B5B5B5; color: black; }
        tr.runcache td a:link { color: black; }
        tr.runcache td a:visited { color: black; }

        tr.tracker td { background-color: white; color: black; }
        tr.tracker td a:link { color: black; }
        tr.tracker td a:visited { color: black; }

        td.numericvalue { text-align: right; }

        tr.paramTitle td { background-color: #4A4A4A; color: white; }

        tr.resultTitle td { background-color: #4A4A4A; color: white; }
        tr.resultTitle td a:link { color: white; }
        tr.resultTitle td a:visited { color: white; }

        tr.param td { background-color: white; color: black; }
        tr.param td a:link { color: black; }
        tr.param td a:visited { color: black; }

        span.selection { background-color: #EBEBEB; }
    </style>
</head>
<body>
    <div id="calculation"></div>
    <div id="details" style="padding-left: 0.1in; display: none;"></div>
</body>
</html>

Edit #2

进一步调查似乎表明CDHtmlDialog类(或其基类)将绘制窗口,无论我的CCalcDrillDownDlg::OnPaint()是否调用CDHtmlDialog::OnPaint(),这只是奇怪而不直观。 :(

此外,似乎这可能与线程相关,因为这似乎取决于渲染窗口所需的时间。如果需要很短的时间,它显示正常。如果需要半秒钟或更长时间,它会搞砸。

目前,我正在使用一种解决方法,我在类中有一个m_bRepaint标志,最初设置为true。在调用CCalcDrillDownDlg::OnPaint()并且它不是标志性的时候,我检查旗帜并强制调整大小。这不是最佳的,因为它会导致初始闪烁,但至少它确保绘制窗口的内容。

    if (!m_bRepaint)
    {
        CDHtmlDialog::OnPaint();
    }
    else
    {
        CRect winRect;
        GetWindowRect(&winRect);
        SetWindowPos(NULL, 0, 0, winRect.Width() - 1, winRect.Height(), SWP_NOMOVE | SWP_NOZORDER);
        SetWindowPos(NULL, 0, 0, winRect.Width()    , winRect.Height(), SWP_NOMOVE | SWP_NOZORDER);
        m_bRepaint = false;
    }

使用Invalidate()不起作用。我必须将其调整为除当前大小之外的其他大小并将其调整大小。

这个CDHtmlDialog类是一个可以使用的PITA,我不建议任何人使用它,如果他们有选择。

答案

好吧,所以看起来这是由于Windows消息队列不是确定性的,所以看起来底层的COM控件在WM_PAINT消息之前在它的DC上绘画。

为了解决这个问题,我等待窗口显示自己等待WM_WINDOWPOSCHANGED消息,发布另一个应用程序消息,然后调用Invalidate()UpdateWindow(),从而强制重绘窗口。

这个技术在Raymond Chen的博客“The Old New Thing”中描述了here

另一答案

不要处理WM_ERASEBKGND。 在对话框上设置WM_CLIPCHILDREN样式。

ON_WM_ERASEBKGND()
...

BOOL CCalcDrillDownDlg::OnEraseBkgnd(CDC* pDC)
{
    // TODO: Add your message handler code here and/or call default
    return TRUE;
}

以上是关于为什么对话框会在不刷新的情况下删除其内容的主要内容,如果未能解决你的问题,请参考以下文章

psql - 如何在不删除表的情况下刷新数据库内容

在不重新加载的情况下向 div 添加/删除内容

我是否可以在不立即呈现更改的情况下更新Firestore文档?

Ajax Javascript 在不刷新页面的情况下不会改变样式

如何在不重新加载整个页面的情况下刷新 div?

在不知道行号的情况下删除特定行