Normal WinForms behavior with modal dialogs seems problematic in apps with multiple top-level windows. I am curious if anyone has developed a more effective approach. Let me explain…
Situation:
When you do dialog.ShowDialog(parent), any attempt by the user to click on or activate that parent window results in activating the modal dialog, and flashing it such that it clearly says “You gotta deal with this first.” Great.
If the user instead clicks on one of the other top-level windows in your app, it won’t do anything… it doesn’t flash the modal dialog… it doesn’t give you a warning… it doesn’t even bring forward the modal dialog… the most you might get is a beep if you don’t have your computer muted. The only thing it does right is that it does NOT activate or bring forward that window you are clicking on… that at least gives the user a clue.
If instead the user uses the Taskbar to select one of your other top-level windows, now it WILL bring it to front! It still won’t activate, but now it is likely fully covering that modal dialog. Any attempt to interact with that top-level window fails. It seems to have locked up. So, many a user has decided our app has locked up… they end up killing our app in Task Manager and restarting! And reporting it to us as a bug. When in fact they had just opened a modal dialog and needed to return to it.
Questions:
Is there some way to get clicking on or activating any window in our app to cause the modal dialog to come to front and flash (like its parent window will do now)?
Can I parent to a group of windows somehow?
We considered having all modal dialogs topmost… but some of our modal dialogs open other windows… some which should not be topmost, so it gets complicated managing topmost. Plus it is unfriendly with other apps.
In an app with multiple top-level forms, how do you prevent the user from thinking it locked up due to a buried modal dialog?
This answer is inspired by Reza's answer above, but Reza's answer does not seem to handle the case where a Modal Dialog opens a non-Modal Form. We now call the following function from the Activated event handlers of our base Forms.
/// <summary>
/// Looks for the last Modal Form in OpenForms that is AFTER justActivatedForm;
/// if it finds it, then it Activates it and returns it. Otherwise, it returns null.
/// </summary>
/// <param name="justActivatedForm">The Form that was just Activated, prompting this check for Modal.</param>
/// <returns>Returns the last Modal Form AFTER justActivatedForm; or null if no such exists.</returns>
public static Form ActivateLastModalForm(Form justActivatedForm)
{
// Is there a Modal Form after justActivatedForm? If so, get the last Modal Form.
Form lastModal = null;
bool foundJustActivatedForm = false;
foreach (Form form in Application.OpenForms)
{
if (foundJustActivatedForm)
{
if (form.Modal)
lastModal = form;
}
else if (form == justActivatedForm)
{
foundJustActivatedForm = true;
}
}
// If last Modal Form is found after justActivatedForm, Activate it
if (lastModal != null)
{
LOG.Focus("Found Modal Form. Activating it...");
if (lastModal.WindowState == FormWindowState.Minimized)
lastModal.WindowState = FormWindowState.Normal;
lastModal.Activate();
}
return lastModal;
}
Please let us know if you see any flaws in this solution!