Fog Creek Software
Discussion Board




Modal dialogs with Win32 API?

I have been charged with the task of making a DLL in VB6 that can display internal dialog boxes as "Modal" to an external window using only the external window's hWnd.

Reason being that this DLL will have to be called from apps written in other languages like VB.Net/C#/C++.  Since I can't count on the calling apps to be able to pass me a VB.Form object, I will implement an hWndOwner property that can be set by the calling app.

OK, so I already know about the SetWindowPos and the EnableWindow (doesn't work for me) Win32 API functions, but I'm only able to make my dialog "System" modal with SetWindowPos, not modal to only one parent window.  Furthermore, theres this issue of a Modal message loop...I have to stop execution of the calling Window while the dialog is being displayed, so I sort of hacked this with a loop like this:

Do While Me.Visible
    DoEvents
Loop

but I don't like it.  I was also thinking about temporarily doing "Application" modal until I can figure out the other way, by just doing this:

Dialog.Show vbModal    ' No owner window

Anyone have a suggestion?  (BTW, I can do some C/C++ stuff if that's an option, but I'm not versed very well in Win32/ATL, otherwise I might know this).

Wayne
Saturday, January 17, 2004

Did you SetParent?

Joel Spolsky
Saturday, January 17, 2004

I had been trying SetParent but I must be missing something else because as soon as I called it, my dialog form would disappear.  I wasn't sure if I should show/zorder the dialog form before or after I call it.

I will have to read some more on it, thanks for the pointer.

For now, I have this code that works although it's not very elegant, I'm sure:

Public Sub ShowModal(oDialog As VB.Form, lHwndOwner As Long)
   
    On Error GoTo ProcErr
       
    Set moDialog = oDialog
   
    AttachMessage Me, lHwndOwner, WM_ACTIVATEAPP
   
    With moDialog
        .Show
        .ZOrder
    End With
   
    EnableWindow lHwndOwner, APIFalse  ' Disable parent.
   
'  Make this work just like a real call to
'  "Form.Show vbModal, frmOwner" where the
'  processing stops right here:
    Do While moDialog.Visible
        DoEvents
    Loop
   
'  Cleanup:
    Debug.Print "Cleaning Up"
    DetachMessage Me, lHwndOwner, WM_ACTIVATEAPP
'  Re-enable the parent form.
    EnableWindow lHwndOwner, APITrue   
   
    BringWindowToTop lHwndOwner
   
    Set moDialog = Nothing
   
  ...

When the WM_ACTIVATEAPP message is sent to this subclassing-object, it just consumes it and calls moDialog.zorder to fix the order.

Luckily :), I can change this implementation later if I find major bugs during testing.

Wayne
Saturday, January 17, 2004

I think that the modal model that Windows has implemented is crap.

I only mention it because I went to bed trying to think around a problem involving modals within mdi forms last night and it really annoys me how poor it is.

An mdi is a great way of managing multiple windows within one application but if the user's doing something in one window that then requires a modal dialog I do not want all the other bloody windows in the application locked too.

Let's say the user is entering data in one of the windows. It brings up a further modal window to ask the user to select from a list, say, of widgets.

Now the only window the user can use is the modal one. But the user isn't sure of the answer and he wants to look up elsewhere in the system... only he can't do anything else in the application.so instead he has to cancel the modal window so that he can go look up the information he will need when he goes back into it.

This is a simple case.. I have situations where the user gets 3 forms deep into a modal and wants to find some information... so there are now 3 modal windows to close before the user can find the information and then has to open them all again.

Microsoft mdi applications suffer from this too... there are quite a few mdi apps where I have to cancel out of what I'm doing to open an unrelated window to find some information... grrrr.

It's always been annoying as a user but now as someone developing a product for users I find that my product is going to provide a crap user experience and it's not my fault!!

Oh, if only someone could show me how to emulate modal dialogs! Properly, where a method is actually paused mid-stream so the local variables are all available afterwards and properly so that we don't end up with a racing CPU (from using DoEvents). This is in .Net by the way

On a side issues, anyone else think Access is a bit shite because the CPU tends to go to 100% when it's in use? It probably doesn't actually matter, just taking up idle CPU but... it looks crap.

Gwyn
Sunday, January 18, 2004

In your situation, don't use modals create child windows.

Modals should only be used for single decisions where the user's intervention is imperative before carrying on. 

Simon Lucy
Sunday, January 18, 2004

It is imperative... but only to what the user's doing in the previous window...

It does not stop the user from wanting to perform a separate unrelated task..

Let's say the user is in the middle of entering data (in his modal dialog) and his boss walks in and asks "where's the figures for xyz".. The user should be able to switch to another window within the application without having to cancel eveything he's in the middle of doing.

Compare it with Windows itself. When Outlook puts up a message saying "Do you want to delete xyz" I can still switch into Explorer or any other application without having to answer that question right there and then... and this is simply the behaviour I want..

And very annoyingly I want to pause my method, go off to the child window and not continue until I've got an answer... this is what modals do and is what I need!

I'm trying to look at alternative window patters... but the mdi is already well-designed for handling multiple child window all conveniently maintained within a master, application, window.

Gwyn
Sunday, January 18, 2004

thats one area that apple has improved considerably in its upgrade to osx.  The original macos modal windows were _system wide_ and nothing could be done in any application until they were dismissed.  MacOSX has added 'drawers' which can be used as a way of having a modal only for the current window, leaving the other windows in the app (and the system) entirely free to be viewed.

much nicer overall.

<g> cant help, just thought it would be interesting to someone.

FullNameRequired
Sunday, January 18, 2004

Explorer and Outlook are different applications (though they share enormous amounts under the hood), which is why one application's modal child window doesn't block the message queue for other applications.

To achieve what you want you need a semaphore and some kind of register within the child modal window class that you create.  When the parent window activates the child window, the child window disallows loss of focus to the parent window but allows it to all other windows.

Hence the need for the register within the child modal class to record the handle of the parent window. 

Simon Lucy
Monday, January 19, 2004

Nearly, but not quite, Simon. You'd need to block change of focus to all children of the parent (except the dialog and its children) as well, not just the parent.


Monday, January 19, 2004

Gwyn, wouldn't it work to create the different MDI windows from different threads, so that each thread has its own modal loop?

Frederik Slijkerman
Monday, January 19, 2004

http://vbnet.mvps.org/index.html?code/forms/mdiownedwindows.htm

This might be helpful

DJ
Wednesday, January 21, 2004

*  Recent Topics

*  Fog Creek Home