Link to home
Start Free TrialLog in
Avatar of BlearyEye
BlearyEyeFlag for United States of America

asked on

Display or hiding all forms associated with a thread

In C# 4.0 I have an application that starts several threads. Each thread performs processing, opening and closing its own forms as necessary.

Each of these threads is intended to run independently (a dashboard using the Observer pattern summarizes the status of each thread). I want to be able to hide all the forms on a given thread and later make them all visible again, including any changes that might have taken place (forms opened or closed).

Right now what I'm doing is using Application.OpenForms. I can associate most of the open forms with a thread, so I can go thru the collection and display or hide the forms for a given thread. It's not perfect, but seems to be good enough.

The main problem seems to be that I can't preserve the z-order of the forms, so that when they are made visible, they're in the order of creation, not necessarily the z-order at the time of hiding. If I could record the current z-order state I'd probably be OK but I don't see how to do that.

On a technical note, I'm not really making the forms visible or invisible; I'm using TopMost and BringToFront() and SendToBack() to selectively move forms into or out of view with respect to a full screen form that acts as a visibility barrier (stuff on top is visible, stuff behind is not). I tried using Visible to hide or display forms, but recording the visibility state when hiding, and handling it when a form on a hidden thread was created turned out to be a bit tricky. Both approaches have the problem of distorting the z-order.

There are other problems with my OpenForms approach, but that's probably enough for now. I'm open to suggestions, either avoiding OpenForms entirely, or somehow dealing with the z-order problem.
Avatar of zapacila89
zapacila89

Why don't you retain the index the form had in the OpenForms collection when you removed it? And when you restore them sort by the that index
Avatar of BlearyEye

ASKER

Given a set of forms in OpenForms, I can't tell their current z-order. All I can tell is the order in which they were created. Operations can change the z-order after they're created.
You can use the windows API function "EnumWindows" to find ALL windows open on the desktop, and then use the "GetWindowThreadProcessId" function to determine which thread each window belongs to.
The windows are always returned in highest to lowest Z-order, so you will be able to store them in the same order.

There are a number of good EnumWindows examples out there for .Net. You will have to do some P/Invoke, so it is not the easiest thing in the world, but it is not too difficult if you see some good examples.

I would recommend you download and include the following open source library:
http://mwinapi.sourceforge.net/

That provides a very good implementation of EnumWindows.

You would use something like this:

int threadID = ... //this is the id of the thread you want to find the windows for.

SystemWindow[] myWindows = SystemWindow.FIlterTopLevelWindows(
             win => (win.Thread == threadID)
   );

//now myWindows is an array of forms that have the required thread id.
//They are all in order by z-order, so you can store them etc.

foreach(SystemWindow w in myWindows)
    w.Hide();
or
foreach(SystemWindow w in myWindows)
     w.Show();

You can probably also sendtoback or sendtofront.
Please see the code project I've attached.
 
I included a very stripped-down version of the ManagedWindows API (LGPL, http://mwinapi.sourceforge.net/) so that it only includes the SystemWindow class you need to filter open windows.
 
This project nicely demonstrates how you can very easily obtain a list of all windows on the desktop and you can filter them by ThreadID to find only the ones you are interested in.
The windows are always returned in Z-order (Top windows First)
 
So you can store them in that order. Then when you hide/show them, just do it in reverse order so that the ones you show last will be in the top of the z-order.

Here are the instructions: Build and run the project.
Then click "Launch", which will launch a new thread and will create several windows on that thread (you specify how many). Those windows will pop up, and will be color-coded based on which thread they belong to.

Then hit "Refresh Now" on the list, and it will list all open windows that belong to any of the threads that were launched by your program.

Try launching about three threads, each with 3 Forms. Then bring some of the Forms to the top of the Z-order and see how the order in the list changes when you click Refresh.
ASKER CERTIFIED SOLUTION
Avatar of ricovox
ricovox
Flag of United States of America 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
Of course, this isn't exactly what you want because I didn't demonstrate hiding and showing the forms. But you should be able to get a good idea of what is possible.

One more important note: in order for you to figure out which of your OpenForms corresponds to a particular SystemWindow in the list, just use the SystemWindow's hWnd property, which corresponds to a .Net Form's Handle property.
Sweet. I'll give this a try as soon as I can (after Labor Day weekend). It looks like this will solve the z-order issue.

So as far as managing visibility goes, it looks like I'm still on my own to hide or show the forms, right? My main objection to the approach I'm using now is that it assumes that the thread application doesn't manipulate TopMost; this guarantees that only the desired forms are on top of the barrier. If that's not true, my approach can fail.

Using Hide() and Show() on the other hand requires me to modify the source for each instance so that desired visibility status (from the thread app's point of view) is recorded whenever it changes.

Any ideas on managing visibility itself?
I think I see what you are saying.
Just to be clear, you mean that each individual thread will hide and show its forms throughout the process, so you don't want to use hide/show from the control app because you won't know when the "worker" thread has hidden a window or when the "main" thread has done so.

I think that TopMost shoud work fine. It's not an ideal situation, but there's nothing wrong with it. One alternative might be to set the "Opacity" of a form to 0 to hide it or to 1 to show it. I don't believe that Opacity affects the "Visible" property.

...oh, I just thought of this..in the code I sent you, it would be a good idea to search for only "Visible" windows, because there are sometimes hidden windows that are created on one of the threads you are looking for.

you could use this code:


			SystemWindow[] myWindows = SystemWindow.FilterToplevelWindows(
				win => _threadIdList.Contains(win.Thread.Id) && win.Visible
					);

Open in new window

SOLUTION
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
You said:
I think I see what you are saying.
Just to be clear, you mean that  each individual thread will hide and show its forms throughout the  process, so you don't want to use hide/show from the control app because  you won't know when the "worker" thread has hidden a window or when the  "main" thread has done so.
Yes, that exactly the problem.

Will work over your suggestions after the weekend. Thanks.
I haven't tried this yet -- still out of town -- but there's going to be a related issue that I didn't mention originally. A parent thread can create another thread, and that child thread can create forms. Hence I need to deal with the forms on the child thread.

I know I can get a list of the threads associated with the current process (Process.GetCurrentProcess().Threads) and I have a list of the parent threads, so those left over must be children; but I don't know how to associate a child with its parent.

Given I can do that, the second issue is to preserve the z-order of the forms associated with a parent and its children.
SOLUTION
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
Hi BlearyEye,

Thank you for accepting my answer. I'm sorry you had performance issues, and I'd like to help you improve them. The SystemWindow list should be completely built within a matter of milliseconds, even if you have many windows open.

So maybe something else is taking a long time or maybe the list could be built more efficiently. When you used my example project, did it also take a long time? It took only milliseconds for me. I would be happy to review some of your code that is relevant to this discussion to see if I can improve it.

Also, as I suggested before, you can use Opacity to "hide" or "show" a form (set Opacity to 0 to hide and to 1 to show a form). That way you can track which forms are hidden or shown, and it is very unlikely that the forms will set opacity themselves. By using opacity, you do not have to use Z-order.

Thanks!
That's very kind of you. Yes, the problem occurred with the sample code you sent me.

I just modified it to measure & display the elapsed time (see code below). It takes about 2.5 seconds, give or take a bit. Will send a screencast separately.
private void btnRefresh_Click(object sender, EventArgs e) {
            DateTime begin = DateTime.Now;
	    RefreshList();
            DateTime end = DateTime.Now;
            TimeSpan diff = end - begin;
            DateTime diffTime = new DateTime(diff.Ticks);
            string totalTime = diffTime.ToString("ss.fff");
            lstWindows.Items.Add("Time required: " + totalTime);
		}

Open in new window

Here's the screencast.
BlearyEye-363001.flv
I tried FindWindowsByThreadID on a Win 7 machine (I'm using Vista). Lo and behold, the performance was snappy; about 0.2 seconds.

Since the target for my application is Win 7, I'm going to go ahead and try this solution.