Not modeless enough (or, maybe, how much modeless can you afford? ;-)

Locked Not modeless enough (or, maybe, how much modeless can you afford? ;-)

  • Thursday, May 03, 2012 2:02 AM
     
     

    Because of the good advice of you helpers, I have been able to construct a modeless dialog box 
    with a button and a progress bar, and some static text, all of which work well by themselves
    in a test program. Thanx for your time.

    Problem is, when I put it all into my application, it is sort of not modeless enough.

    After the test, as sort of an in situ test, I invoked the dialog at the end of my App::InitInstance, with SW_SHOW. 
    It could be moved around, showed its static text, button, and progress bar.

    The following snippet shows how I now want to start it normally:

    Terminator *guard; // guard is static in MyApp.h, with an access function. 
    // Terminator class code comes later

    BOOL MyApp::InitInstance() {
    ..
    ..


      guard= new Terminator(AfxGetMainWnd());
      guard->Create(IDD_TERMINATOR, NULL);
      guard->ShowWindow(SW_HIDE);
    return(TRUE);
    } // MyApp::InitInstance


    class Terminator : public CDialog {
    DECLARE_DYNAMIC(Terminator)
    public:
    Terminator(CWnd* pParent = NULL); // standard constructor
    virtual ~Terminator();
    // Dialog Data
    enum { IDD = IDD_TERMINATOR };
    protected:
    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
    DECLARE_MESSAGE_MAP()
    public:
    afx_msg LRESULT ShowProgress(UINT pct);
    CProgressCtrl marcher; // the progress bar
    afx_msg void OnBnClickedStop();
    virtual BOOL OnInitDialog();
    CButton term_button;
    }; // class Terminator

    --------------------------------------------
    // Terminator.cpp

    #include "stdafx.h"
    #include "Terminator.h"
    #include "afxdialogex.h"
    #include "resource.h"
    #include "bond_log.h"
    #include "kb.h"
     
    IMPLEMENT_DYNAMIC(Terminator, CDialogEx)

    Terminator::Terminator(CWnd* pParent /*=NULL*/)
    : CDialog(Terminator::IDD, pParent) {
    } // Terminator constructor

    Terminator::~Terminator() {
    } // Terminator destructor

    void Terminator::DoDataExchange(CDataExchange* pDX) {
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_PROGRESS1, marcher);
    DDX_Control(pDX, ID_STOP, term_button);
    } // Terminator::DoDataExchange

    BEGIN_MESSAGE_MAP(Terminator, CDialog)
    ON_BN_CLICKED(ID_STOP, &Terminator::OnBnClickedStop)
    END_MESSAGE_MAP()

    // Terminator message handlers 

    void Terminator::OnBnClickedStop() {
    put_terminate_compilation(TRUE); // Sets a flag called terminate_compilation 
    // in the data segment of another module.
    exclaim1("INFO", "Bundle compilation terminated manually by user");
    // I see this when I push the  TERMINATE pushbutton, when it works.
    // exclaim1 is my wrapping of MessageBox, sort of.
    CDialog::OnOK();
    } // Terminator::OnBnClickedStop
     
    LRESULT Terminator::ShowProgress(UINT pct) {
    marcher.SetPos(pct);
    return(0);
    } // Terminator::ShowProgress  
     
    BOOL Terminator::OnInitDialog() {
    CDialog::OnInitDialog();

    marcher.SetRange(0, 100);
    marcher.SetBarColor(RGB(255,0,0));
    term_button.ShowWindow(WS_VISIBLE);
    return(TRUE); 
    } // Terminator::OnInitDialog
    ------------------------------------------------

    Here is the relevant snippet from ..........rc:

    IDD_TERMINATOR DIALOGEX 0, 0, 245, 110
    STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
    CAPTION "Terminate compilation"
    FONT 8, "MS Shell Dlg", 400, 0, 0x1
    BEGIN
        DEFPUSHBUTTON   "TERMINATE",ID_STOP,86,45,50,14
        LTEXT           "To terminate compilation, push the button.",IDC_STATIC,21,23,153,8
        CONTROL         "",IDC_PROGRESS1,"msctls_progress32",WS_BORDER,10,77,227,14
    END

    --------------------------------------------------

    The problem begins when I bring up the dialog and then start up a computation,
    as follows:

    void make_bundles() {
    ..
    ..
      get_guard()->ShowWindow(SW_SHOW); // get_guard is an access function
      enumerate_something(param); // VERY INTENSIVE AND LONG COMPUTATION INVOLVING EXHAUSTIVE SEARCH
    // Sit here and spin until enumerate_something is done.
    while (!enumerated);
    if (get_guard() != NULL) get_guard()->ShowWindow(SW_HIDE);
      return;
    } // make_bundles

    enumerate_something does the follwing:

    bunch of computation, in loop.
    formation_counter++;
    if (formation_counter == 100) {
       if (get_guard()) {
    get_guard()->ShowProgress(set_number * fraction_of_bar);
    }
    formation_counter= 0;
    }

    get_guard()->ShowProgress causes a very nice red bar to crawl across the progress bar of the dialog.
    UNFORTUNATELY:
    1) The dialog is now all locked up- I can't move it modelessly.
    2) I can't see the static text.
    3) I can't see the "TERMINATE" button, so I can't stop the computation
    until it is finished, which could be a long long time, forcing me to kill
    the whole thing with the task manager.

All Replies

  • Thursday, May 03, 2012 3:42 AM
     
     Answered

    The foundation of a GUI program is its message pump. If you don't let the message pump run then the GUI can't run. That means you cannot do lengthy computation loops, and you cannot do a spin loop.  You must let the message pump respond to user input (and painting updates).  That is rule 1 and you are ignoring it.

    Move such lengthy work into secondary threads. That lets you compute intensley while concurrently maintaining the GUI. 

    Multithreading is too big a topic to cover in a forum post. But you can get an education on it from this site:

    http://www.flounder.com/mvp_tips.htm

     

  • Thursday, May 03, 2012 5:43 AM
     
     

    I took out the spin loop, and put in a message box after the computation. So, I see the static text and the button, but now nothing in the dialog will respond until I answer the message box. That is, it seems that the dialog is no longer modeless. The intensive computation is no longer happening- I just let it terminate on its own.

    Many times I have used this kind of progress dialog with termination, and the marching progress bar is actually happening because there is a computation going on.

    Might there be something else happening? If I initialize my app, and SW_SHOW the dialog, and don't trigger my intensive search computation, then I have a modeless dialog where I can have both that dialog and a responding message box both up at the same time, in parallel, as well as responses from menu picks.

    I have tried running the search in a thread, for very short computations. The thread never returns the "done" flag.

  • Thursday, May 03, 2012 3:24 PM
     
     

    A message box is modal, and while it is up it runs a message loop so that the app's painting updates can continue to occur. But being modal, it blocks user input to other parts of the GUI.