How can I show a modeless dialog and display infor

2019-04-08 06:56发布

问题:

I want to show a modeless dialog on the screen and display some information in it.

However if I use it the following way, it has some problems:

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

It seems the dialog displayed, but it does not draw any information in it. It only draw all information when the function is over.

How can I modify the modeless dialog so it will display the information immediately?

回答1:

There are a few things you can do.

(1) You could post the dialog a message from inside the CDialog::OnInitDialog method and then handle the long function in the message handler of that posted message. That way the dialog will first be displayed and then later the long function will get run.

(2) The second option is to make sure the message loop gets some processing time. So if your long function is some sort of loop just add the occasional call to the ProcessMessages to make sure the message queue is kept empty:

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

Edit: It certainly is possible to use threads is such a situation, but doing so is not always without risk and complexity.

Using threads with a GUI means having to deal with multiple message queues which then means using API's like PostThreadMessage and that introduces a new set of issues to be wary of.

For an example of one such issue refer to this link:

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

where is says:

Messages sent by PostThreadMessage are not associated with a window. As a general rule, messages that are not associated with a window cannot be dispatched by the DispatchMessage function. Therefore, if the recipient thread is in a modal loop (as used by MessageBox or DialogBox), the messages will be lost. To intercept thread messages while in a modal loop, use a thread-specific hook.

I use the process message approach in the Zeus IDE and it works very well at making sure the GUI remains responsive to the user. It is also has the advantage of being very easy to implement.



回答2:

In OnInitDialog, start a worker thread to perform the computations. Post a user message from the worker thread to update the dialog.

This is superior to the ProcessMessages implementation for several reasons:

  • The code for doing the calculations can be separated out of the UI code, where it does not belong.

  • The UI remains responsive while the actual calculations are being performed. ProcessMessages allows multiple UI updates during the single calculation function, but the UI will still be blocked during the actual calculations.

Dialog code:

#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;
}

The worker thread:

UINT MyCountFunc(LPVOID lParam)
{
    HWND hDialogWnd = (HWND)lParam;

    for (int i=0; i < 100; ++i)
    {
        PostMessage(hDialogWnd, WM_NEW_COUNT, i, NULL);
    }
}


回答3:

As a rule of thumb, heavy computations should never be placed in the GUI thread. Since it is a modeless dialog, the dialog will not own the message loop. The ProcessMessage() solution will work, but is IMO not the right way. My suggestion is: 1) Spawn a new thread in OnInitDialog() 2) Have the separate thread post messages to the dialog when something interesting happens. One of these interesting things is that the work is done.

Note, however, that this will mean that you need to perform proper synchronization.



回答4:

Don't try to do your heavy work all at once. Have the dialog post itself a message in the WM_APP range in OnInitDialog. The WM_APP handler can do part of the heavy work, then do another PostMessage and return. In this way, you allow the message pump to process window messages in between your chunks of processing.