Link to home
Start Free TrialLog in
Avatar of tfsln
tfslnFlag for New Zealand

asked on

How can i get the topmost form?

I have a background worker process which runs in the background (obviously). If the operation fails, i need a messagebox to pop up as a modal dialog owned by the currently open window (so that it doesnt disappear behind other windows).

My experience tells me that the Form.ActiveForm property does not always return a value.

I need a more reliable way of estabishing which open form should be the owner for the dialog window.

All windows in the application are displayed as modal. So we have a "stack" of windows open at any given time, and i need to return the topmost window on this stack.

Alternatively - if i set the owner to the first window in the "stack", will that result in the dialog being displayed above all other child windows?
Avatar of abel
abel
Flag of Netherlands image

First, let's answer the story about the owner window.

without an owner window and .Show
this will open the window with as owner window the desktop. That means that window can be hidden or placed at any level in the z-order by user interaction of focusing the windows.

The underlying window that opened the form has nothing to do with the opened window (except that it can keep a handle to it for closing the window programmatically). If the "parent" window is closed, the opened window will remain.

with an owner window and .Show(this)
this will open the window with as owner the specified window. This will always keep the window higher in the z-order then the owner window, which effectively means that the child window will always be obfuscating parts of the parent window.

The underlying window that opened the form can still be accessed by the user. However, when he moves it, the child window will remain visible. Closing the parent window will also close the child window.

without an owner window and .ShowDialog
this will open the window with as owner the desktop, you would think. But in fact, the CLR will designate the opening window as the parent. This is effectively the same as the following:

with an owner window and .ShowDialog(this)
this will open the window with a owner the specified window. The opening window, the parent, is not accessible anymore if the user tries to select it. Closing the parent window (by a timer or something) will close the child, but it is common practice to first close the model children.

stacking windows with .Show and .Show(this)
suppose you open a chain of windows with an owner window, and then the last one without an owner window. Now, as soon as you select any off the parent windows, this last window will be hidden behind all the other windows.

stacking windows with  .Show(this)
opening a chain of windows with an owner window and closing any of the parents (at any level) will always close all its descendant (lower in z-order) children.

.ActiveForm and modal windows
the active form is the form that receives user input. You can have a modal window, which will always be returned by ActiveForm because it is blocking the underlying windows, but when a user selects another application, ActiveForm will return null.

.ActiveForm and non-modal windows
If you have a chain of non-modal windows, .ActiveForm will show the window that actually has the focus. This is not necessarily the top window, because if the windows are owned, they may obscure the underlying windows, but they won't show as modal.



and now on with your actual question... :)


Avatar of tfsln

ASKER

Cheers for that... im not sure whether this question is covered, but what happens if you have one master window with a stack of child windows like this;

Window1
Window2 (owned by window1 - modal)
Window3 (owned by window2 - modal)

(when i say modal, i mean opened using ShowDialog as opposed to show)

In this situation, what happens if you open a new modal window from inside the Window1 with the owner as Window1?
You cannot, so that is not a problem. You can only open a new modal window from within the topmost modal window.

Your main part of the question, determining the top window, appears not trivial at all. There's no property you can use and there's no simple way to calculate the z-order from one to another, not even if your loop through all visible forms.

If there's one chain of opening windows with one root, and each of them have the owner property set, then it becomes quite easy:

foreach (Form frm in Application.OpenForms)
{
                if(frm.OwnedForms.Length == 0)
                    // do something with the topform
}

however, if you use several .Show(this) from your main form, then you get a certain tree with several branches:

frm1 -- window1
   new MyForm().show(frm1)  -- window2
       new MyForm().show(this) --- window3
    new MyForm().show(frm1) -- window4

now the user can click on window3 or window4 to bring them to the top. Both of them do not have owned forms (i.e., have the highest z-order), but there relative position is not clear.

To get the correct relative position, I think you need to do old-school Win32 API and send a GetWindowPos, which will also retrieve the z-order.
> You cannot, so that is not a problem.

to be a bit clearer on that subject: in general, you cannot and should not. But there are "workarounds". But the following:

Form frm1 = new SomeForm();
frm1.ShowModal(this);
Form frm2 = new SomeForm();
 frm2.ShowModal(this);

will block execution until the first ShowModal finishes. So no two modal popups at the same time.

Now, if you would do something through an asynchronous timer call for instance, or you would open a window in the form_load that is an instance of self, then you will find that it actually is possible. Until you manage to crash Visual Studio (what I just did by opening too many modal windows... after hitting Break, VS did not recover and without warning disappeared).
 

Avatar of tfsln

ASKER

> Now, if you would do something through an asynchronous timer call for instance

Thats exactly what is happening in my situation - but i would have a check to make sure that the window wasnt open already (by using the Application.OpenForms collection) and if it was then i would not attempt to open a new one.

The issue comes when trying to establish what to pass as the 'owner' parameter of the ShowDialog() function to ensure that the form is displayed on top of the current active window... you may be right i may need to use some old school api calls...
ASKER CERTIFIED SOLUTION
Avatar of abel
abel
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of tfsln

ASKER

Thats cool ive got all the information i require cheers