How can I show a modeless dialog and display information in it immediately?
Asked Answered
C

5

6

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?

Celibate answered 22/12, 2008 at 10:16 Comment(0)
A
6

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.

Aweigh answered 22/12, 2008 at 10:49 Comment(10)
IMO the ProcessMessage() function is the way to add responsiveness to a MFC app.Teach
I too would use the ProcessMessage option ;)Aweigh
I strongly disagree. 1) A UI method should not be performing a lot of "other" work, the concerns should be separated. 2) A simple worker thread can be used in most of these situations, no need for multiple pumps. Why are you trying to scare the OP away from threads?Chrysler
Did you read my reply? When you mix threads and GUIs windows messages can and do go missing if the code is not correct. Why mess with message hooks if you don't need to? Add in COM and you get another layer of complexity: msdn.microsoft.com/en-us/library/ms693344(VS.85).aspxAweigh
I've been there, done that and it is not the walk in the park you seem to think it is. For most case processing the message queue works just as well and it is very simple solution. As I said I use it all the time and it works just fine. And I say again it is very very simple to implement.Aweigh
Windows messages do not "go missing." COM has nothing to do with this question. A worker thread for computations that posts to the UI is the standard, tried-and-true way to accomplish the OP's goal.Chrysler
ajryan writes: Windows messages do not "go missing." Can you read: "if the recipient thread is in a modal loop (as used by MessageBox or DialogBox), the messages will be lost" Once again try reading: "To intercept thread messages while in a modal loop, use a thread-specific hook."Aweigh
ajryan writes: A worker thread for computations that posts to the UI is the standard If it is so simple, stock standard and staight forward, why not post some source code showing how a MFC dialog would use a thread to load during OnInitDialog. Or do you just like talking out of your arse?Aweigh
Re: if recipient is in a modal loop, the title of this question is "... modeless dialog."Chrysler
Also, no need for PostThreadMessage. You're making a simple problem more complicated than it is. And there's no need to get personal.Chrysler
C
5

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);
    }
}
Chrysler answered 23/12, 2008 at 14:14 Comment(0)
K
2

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.

Kuwait answered 22/12, 2008 at 12:44 Comment(0)
C
1

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.

Confound answered 24/12, 2008 at 4:36 Comment(0)
L
0

In SDI and MDI applications, the ProcessMessages() function provided will not cause ON_UPDATE_UI messages to be generated for the status bar. The status bar is often used to report information about "where the mouse is", as it pertains to the document being viewed or edited. While WM_MOUSEMOVE messages will be routed successfully, the status bar updates won't. What's needed is an explicit call to the status bar's OnUpdateCmdUI() method - after pumping the message loop would be a reasonable place to call it. e.g.in your CMainFrame, add:

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

and then call it in ProcessMessages:

...
static_cast<CMainFrame*>( AfxGetApp()->m_pMainWnd )->ForceStatusBarUpdate();
Le answered 4/5 at 16:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.