如何显示非模式对话框并立即在其中显示信息?

问题描述 投票:0回答:5

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

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

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

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

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

mfc dialog modeless
5个回答
6
投票

您可以做一些事情。

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

(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 调度 功能。因此,如果收件人 线程处于模态循环中(如 消息框或对话框), 消息将会丢失。拦截 在模态中线程消息 循环,使用特定于线程的钩子。

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


5
投票

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

这优于 ProcessMessages 实现,原因如下:

  • 进行计算的代码可以从 UI 代码中分离出来,它不属于 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);
    }
}

2
投票

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

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


1
投票

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


0
投票

在 SDI 和 MDI 应用程序中,提供的 ProcessMessages() 函数将不会导致为状态栏生成 ON_UPDATE_UI 消息。状态栏通常用于报告有关“鼠标所在位置”的信息,因为它与正在查看或编辑的文档有关。虽然 WM_MOUSEMOVE 消息将成功路由,但状态栏不会更新。需要的是对状态栏的 OnUpdateCmdUI() 方法的显式调用 - 在泵送消息循环之后将是调用它的合理位置。 例如,在您的 CMainFrame 中,添加:

void CMainFrame::ForceStatusBarUpdate()
{
    m_wndStatusBar.OnUpdateCmdUI(this,FALSE);
}

然后在ProcessMessages中调用它:

...
static_cast<CMainFrame*>( AfxGetApp()->m_pMainWnd )->ForceStatusBarUpdate();
© www.soinside.com 2019 - 2024. All rights reserved.