Learn how to a build a cloud-first strategyRegister Now

  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 488
  • Last Modified:

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?
  • 4
  • 3
1 Solution
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... :)

tfslnAuthor Commented:
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;

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.
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

> 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();
Form frm2 = new SomeForm();

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).

tfslnAuthor Commented:
> 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...
I've been puzzled by this for over an hour now. Normally I'm quite handy with these things. Though it is easy to get the top level window that has the focus, even if ActiveForm would return null, it is far from trivial to get the window that is on top of the (visible) z-order. This is for a large part due to the method in which Windows arranges the windows amongst each other (parent/child and owner/owning) and that in the parlor of Windows, the topmost window is the window with the focus, even if it doesn't have the focus...
 In other words, to find the top window (the one not obscured by others) that is not necessarily the input window, is daunting to say the least. Strange as it can get, there is a SetWindowPos which includes a value for the z-order, but it the opposite, GetWindowPos does not exist
 A quick way of dealing with this problem is apparently just using a loop through consecutive FindWindowEx calls.
tfslnAuthor Commented:
Thats cool ive got all the information i require cheers

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now