【问题标题】:How can I show a modeless dialog and display information in it immediately?如何显示无模式对话框并立即在其中显示信息?
【发布时间】:2010-09-28 00:10:41
【问题描述】:

我想在屏幕上显示一个无模式对话框并在其中显示一些信息。

但是,如果我按照以下方式使用它,它会出现一些问题:

function()
{
showdialog(XXX).
//heavy work.
update the dialog..
//heavy work.
update the dialog...
}

似乎显示了对话框,但它没有在其中绘制任何信息。仅在函数结束时绘制所有信息。

如何修改无模式对话框,使其立即显示信息?

【问题讨论】:

    标签: mfc dialog modeless


    【解决方案1】:

    您可以做一些事情。

    (1) 您可以从 CDialog::OnInitDialog 方法中向对话框发布一条消息,然后在该发布消息的消息处理程序。这样,对话框将首先显示,然后长函数将运行。

    (2) 第二个选项是确保消息循环获得一些处理时间。因此,如果您的长函数是某种循环,只需将偶尔调用添加到 ProcessMessages 以确保消息队列保持为空:

    void ProcessMessages()
    {
        MSG msg;
        CWinApp* pApp = AfxGetApp();
        while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            pApp->PumpMessage();
        }
    }
    

    编辑:在这种情况下当然可以使用线程,但这样做并不总是没有风险和复杂性。

    使用带有 GUI 的线程意味着必须处理多个消息队列,这意味着使用像 PostThreadMessage 这样的 API,并且引入了一系列需要警惕的新问题。

    有关此类问题的示例,请参阅此链接:

    http://msdn.microsoft.com/en-us/library/ms644946(VS.85).aspx

    在哪里说:

    PostThreadMessage 发送的消息是 与窗口无关。作为一个 一般规则,不是 不能与窗口关联 由 DispatchMessage 调度 功能。因此,如果收件人 线程处于模态循环中(由 MessageBox 或 DialogBox), 消息将丢失。拦截 在模态中线程消息 循环,使用线程特定的钩子。

    我在Zeus IDE 中使用了进程消息方法,它可以很好地确保 GUI 保持对用户的响应。它还具有非常容易实现的优点。

    【讨论】:

    • IMO ProcessMessage() 函数是为 MFC 应用程序添加响应能力的方法。
    • 我也会使用 ProcessMessage 选项 ;)
    • 我强烈反对。 1) UI 方法不应该执行很多“其他”工作,应该分离关注点。 2) 一个简单的工作线程可以在大多数这些情况下使用,不需要多个泵。你为什么要让 OP 远离线程?
    • 你看我的回复了吗?当您混合线程和 GUI 时,如果代码不正确,Windows 消息可能而且确实会丢失。如果不需要,为什么要弄乱消息挂钩?添加 COM,您将获得另一层复杂性:msdn.microsoft.com/en-us/library/ms693344(VS.85).aspx
    • 我去过那里,做过那件事,这不是你想象中的公园散步。对于大多数情况下,消息队列的处理效果也很好,而且它是非常简单的解决方案。正如我所说,我一直在使用它,而且效果很好。我再说一遍,实现起来非常非常简单。
    【解决方案2】:

    在 OnInitDialog 中,启动一个工作线程来执行计算。从工作线程发布用户消息以更新对话框。

    这优于 ProcessMessages 实现有几个原因:

    • 进行计算的代码可以从不属于它的 UI 代码中分离出来。

    • 在执行实际计算时,UI 保持响应。 ProcessMessages 允许在单个计算函数期间进行多次 UI 更新,但在实际计算过程中 UI 仍然会被阻塞。

    对话框代码:

    #define WM_NEW_COUNT (WM_USER + 0x101)
    
    BEGIN_MESSAGE_MAP()
        ON_MESSAGE(WM_NEW_COUNT, OnNewCount)
    END_MESSAGE_MAP()
    
    BOOL CMyDialog::OnInitDialog()
    {
        CWinThread* pThread = AfxBeginThread(MyCountFunc, this->GetSafeHwnd());
        return TRUE;
    }
    
    LRESULT CMyDialog::OnNewCount(WPARAM wParam, LPARAM)
    {
        int newCount = (int)wParam;
    
        // m_count is DDX member, e.g. for label
        m_count = newCount;
    
        UpdateData(FALSE);
    
        return 0;
    }
    

    工作线程:

    UINT MyCountFunc(LPVOID lParam)
    {
        HWND hDialogWnd = (HWND)lParam;
    
        for (int i=0; i < 100; ++i)
        {
            PostMessage(hDialogWnd, WM_NEW_COUNT, i, NULL);
        }
    }
    

    【讨论】:

      【解决方案3】:

      根据经验,不应将繁重的计算放在 GUI 线程中。因为它是一个无模式对话框,所以对话框不会拥有消息循环。 ProcessMessage() 解决方案将起作用,但 IMO 不是正确的方法。我的建议是: 1) 在 OnInitDialog() 中产生一个新线程 2)当有趣的事情发生时,让单独的线程向对话框发布消息。这些有趣的事情之一是工作已经完成。

      但是请注意,这意味着您需要执行正确的同步。

      【讨论】:

        【解决方案4】:

        不要试图一次完成繁重的工作。让对话框在 OnInitDialog 的 WM_APP 范围内发布消息。 WM_APP 处理程序可以完成部分繁重的工作,然后执行另一个 PostMessage 并返回。通过这种方式,您允许消息泵在您的处理块之间处理窗口消息。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多