Modeless Dialog Insanity - Programmer

This is a discussion on Modeless Dialog Insanity - Programmer ; Hi All, I have spent the last two days *trying* to construct a modeless dialog box that operates how I want and I am on the point of throwing my keyboard through my monitor. No, seriously. Here is what I ...

+ Reply to Thread
Results 1 to 6 of 6

Thread: Modeless Dialog Insanity

  1. Modeless Dialog Insanity

    Hi All,

    I have spent the last two days *trying* to construct a modeless dialog
    box that operates how I want and I am on the point of throwing my
    keyboard through my monitor. No, seriously.

    Here is what I am trying to do. I want there only ever to be one
    instance of the dialog, and if the user tries to open it again from
    the menu it should just bring the one that already exists into focus.
    No problem I hear you say, and I partially agree with you. What I
    have done is set the pointer "modeless_dialog" to zero in the main
    frame constructor, and then used the following code to test to see if
    its zero or not:

    void CMainFrame::OnViewModelessdialog()
    {
    if (!modeless_dialog) {
    modeless_dialog = new CModelessDlg;
    modeless_dialog->Create(IDD_DIALOGMODELESS,this);
    modeless_dialog->ShowWindow(SW_SHOW);
    }
    else
    modeless_dialog->SetFocus();
    }

    This works *great*, until you close the modeless dialog and then try
    to open it again. Now I know why it crashes - as part of my modeless
    dialog I have:

    void CModelessDlg::PostNcDestroy()
    {
    CDialog::PostNcDestroy();
    delete this;
    }

    so when you close the dialog, the dialog object is destroyed, but the
    pointer to it is not - ie "modeless_dialog" now useless. This causes
    SetFocus() to fall over the next time it is called.

    So what I need to do is to send a message to the main frame and have
    that message set modeless_dialog back to zero. So far so good -
    problem diagnosed, and now for the solution. I really want to send
    the message from PostNcDestroy, as that is the last thing called
    before the dialog is terminated. So I set up a ON_MESSAGE message
    back to the main frame window that looks like this:

    LRESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam) {
    modeless_dialog = 0;
    return 0;
    }

    and then I modified my PostNcDestroy to:

    void CModelessDlg::PostNcDestroy()
    {
    CDialog::PostNcDestroy();
    GetParent()->PostMessage(WM_MYMESSAGE,0,0);
    delete this;
    }

    Now however the program crashes when you attempt to close the dialog.
    Now I was using the same message to display a messagebox when you push
    a button on the dialog before, so I know that the changes I have made
    to the message map etc are fine. The problem is definitely in the
    line:

    GetParent()->PostMessage(WM_MYMESSAGE,0,0);

    but I honestly have no idea how to find out whats causing the problem.
    Its as if the GetParent() function is invalid when used in the
    PostNcDestroy function.

    What makes this particularly galling is that this is the exact same
    technique as detailed in a tutorial for beginners on the CodeProject!
    If anyone can suggest anything on this I would really appreciate it.

  2. Re: Modeless Dialog Insanity


    > and then I modified my PostNcDestroy to:
    >
    > void CModelessDlg::PostNcDestroy()
    > {
    > CDialog::PostNcDestroy();
    > GetParent()->PostMessage(WM_MYMESSAGE,0,0);
    > delete this;
    > }
    >
    > Now however the program crashes when you attempt to close the dialog.
    > Now I was using the same message to display a messagebox when you push
    > a button on the dialog before, so I know that the changes I have made
    > to the message map etc are fine. The problem is definitely in the
    > line:
    >
    > GetParent()->PostMessage(WM_MYMESSAGE,0,0);
    >
    > but I honestly have no idea how to find out whats causing the problem.
    > Its as if the GetParent() function is invalid when used in the
    > PostNcDestroy function.


    PostNcDestroy simply is a little bit late to do this. The dialog's window handle
    has been detached from the CModelessDlg object at this point already and
    therefore GetParent won't work.

    > If anyone can suggest anything on this I would really appreciate it.


    One solution would be to implement an WM_NCDESTROY message handler
    in your dialog class and move your PostNcDestroy code to this message handler.
    Something like this:

    BEGIN_MESSAGE_MAP(CModelessDlg, CDialog)
    ON_WM_NCDESTROY()
    END_MESSAGE_MAP()

    void CModelessDlg::OnNcDestroy ()
    {
    GetParent()->PostMessage(WM_MYMESSAGE,0,0);
    CDialog::OnNcDestroy ();
    }

    Note that order of the two statements matters. The OnNcDestroy base class
    implementation detaches the window handle (and then calls PostNcDestroy).

    Regards,
    Frank



  3. Re: Modeless Dialog Insanity

    Thanks Frank,

    As you rightly pointed out, the problem was that GetParent() was not
    returning anything usefull as by the time I called it there was no
    parent. I solved the problem by moving the message sending up the
    chain into the OnOk() and OnCancel() methods. It now works fine -
    just one dialog at a time, and everything is dandy.

    While I am waving the stupidity flag around however, I do have a
    question regarding the following:

    void CMainFrame::OnViewModelessdialog()
    {
    if (!modeless_dialog) {
    modeless_dialog = new CModelessDlg;
    modeless_dialog->Create(IDD_DIALOGMODELESS,this);
    modeless_dialog->ShowWindow(SW_SHOW);
    }
    else
    modeless_dialog->SetFocus();
    }

    Now I have declared "modeless_dialog" to be a pointer to a
    CModelessDlg object in CMainFrame, so:

    modeless_dialog = new CModelessDlg;

    is clearly doing nothing more than creating an instance of
    CModelessDlg on the heap and then passing the address to
    modeless_dialog. But how about the next line?

    We already have an instance of the CModelessDlg class that is a member
    of the CMainFrame class, so this line must simply be creating a window
    based off the dialog resource it is passed. My point is that the
    handle passed to Create is only for the actual dialog window that you
    see on the screen, and not for the instance of the CModelessDlg from
    which it is created. Its like the dialog on the screen is a child of
    the class I created. Is this correct?

    The reason I ask is that if I want to make the dialog box a child of
    the desktop window by using:

    modeless_dialog->Create(IDD_DIALOGMODELESS,GetDesktopWindow());

    then how do I ensure that modeless dialog is set to NULL in the main
    frame message handler? What I mean is that:

    LRESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam) {
    modeless_dialog = 0;
    return 0;
    }

    no longer works. The thing is, according to my logic modeless_dialog
    is still a member of CMainFrame and so I should be able to set the
    pointer to zero (or NULL if you prefer), and the only reason why this
    would not work was if modeless_dialog was now a child of the desktop
    window! This is very confusing to a beginner.

    Would someone care to comment?

  4. Re: Modeless Dialog Insanity

    Rodney B. Elliott wrote:
    > While I am waving the stupidity flag around however, I do have a
    > question regarding the following:
    >
    > void CMainFrame::OnViewModelessdialog()
    > {
    > if (!modeless_dialog) {
    > modeless_dialog = new CModelessDlg;
    > modeless_dialog->Create(IDD_DIALOGMODELESS,this);
    > modeless_dialog->ShowWindow(SW_SHOW);
    > }
    > else
    > modeless_dialog->SetFocus();
    > }
    >
    > Now I have declared "modeless_dialog" to be a pointer to a
    > CModelessDlg object in CMainFrame, so:
    >
    > modeless_dialog = new CModelessDlg;
    >
    > is clearly doing nothing more than creating an instance of
    > CModelessDlg on the heap and then passing the address to
    > modeless_dialog. But how about the next line?
    >
    > We already have an instance of the CModelessDlg class that is a member
    > of the CMainFrame class, so this line must simply be creating a window
    > based off the dialog resource it is passed. My point is that the
    > handle passed to Create is only for the actual dialog window that you
    > see on the screen, and not for the instance of the CModelessDlg from
    > which it is created. Its like the dialog on the screen is a child of
    > the class I created. Is this correct?



    No, all you have is a pointer that is a member of the CMainFrame class.
    There is no CModelessDlg object until you call 'new'. Windows is
    perfectly capable of displaying a dialog without MFC. What the Create
    call does is ask Windows to create such a dialog and then it stores the
    HWND in the CModelessDlg object. The class, like all MFC CWnd-derived
    classes, is not a window and does not implement a window. It is only a
    C++ interface layer to a Win32 window. Rather than think of it as a
    parent/child relation, consier it a container/contained relation for the
    convenience of C++ programmers.


    >
    > The reason I ask is that if I want to make the dialog box a child of
    > the desktop window by using:
    >
    > modeless_dialog->Create(IDD_DIALOGMODELESS,GetDesktopWindow());
    >
    > then how do I ensure that modeless dialog is set to NULL in the main
    > frame message handler? What I mean is that:
    >
    > LRESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam) {
    > modeless_dialog = 0;
    > return 0;
    > }
    >
    > no longer works. The thing is, according to my logic modeless_dialog
    > is still a member of CMainFrame and so I should be able to set the
    > pointer to zero (or NULL if you prefer), and the only reason why this
    > would not work was if modeless_dialog was now a child of the desktop
    > window! This is very confusing to a beginner.
    >
    > Would someone care to comment?



    I can only comment here that you are confused. Why do you say "it no
    longer works"? The modeless dialog can be a child of the desktop if you
    like. That has no effect on the code above. To make the code above
    work, the dialog must post the message to the main window (use
    AfxGetMainWnd()). Perhaps you mistakenly have it using GetParent?

    --
    Scott McPhillips [VC++ MVP]


  5. Re: Modeless Dialog Insanity

    How about this:

    Instead of saying new MyDialog();

    Add a static member function called say, static void DisplayDialog():

    MyDialog : CDialog
    {
    public:
    staic void DisplayDialog()
    {
    if ( m_MyDialog )
    {
    // Show current dialog, bring to front etc...
    }
    else
    {
    m_MyDialog = new MyDialog();
    // etc ...
    }
    }

    virtual ~MyDialog()
    {
    delete m_MyDialog;
    m_MyDialog=NULL;
    }

    static MyDIalog m_MyDialog;
    }

    MyDialog::m_MyDialog = NULL;

    That sort of thing - you can then show a dialog with DisplayDialog() and
    access it through MyDialog:m_MyDialog - if it's still availble - but
    ofcourse it might go away while you're using it so you might need to use
    messages here, or some other method of comminication, rather than call on
    the object directly.



  6. Re: Modeless Dialog Insanity

    I may be mistaken, but isn't it simply easiest to trap and handle the
    WM_CLOSE message (and stop the parent class from handling this
    message). In this case your modeless dialog will not actually be
    closed, but rather something such as making it hidden should be done.

    Simon Hayden
    http://www.AutoUpdatePlus.com
    Get software updates to your clients the Quick and Easy way!

+ Reply to Thread