Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

Display or hiding all forms associated with a thread

Posted on 2010-08-31
16
653 Views
Last Modified: 2013-12-17
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.
0
Comment
Question by:BlearyEye
  • 8
  • 7
16 Comments
 
LVL 1

Expert Comment

by:zapacila89
ID: 33572879
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
0
 
LVL 1

Author Comment

by:BlearyEye
ID: 33573044
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.
0
 
LVL 4

Expert Comment

by:ricovox
ID: 33573161
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.
0
MIM Survival Guide for Service Desk Managers

Major incidents can send mastered service desk processes into disorder. Systems and tools produce the data needed to resolve these incidents, but your challenge is getting that information to the right people fast. Check out the Survival Guide and begin bringing order to chaos.

 
LVL 4

Expert Comment

by:ricovox
ID: 33573456
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.
0
 
LVL 4

Accepted Solution

by:
ricovox earned 500 total points
ID: 33573465
Here is the attachment. It didn't upload properly in my previous post.

FindWindowsByThreadID.zip
0
 
LVL 4

Expert Comment

by:ricovox
ID: 33573478
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.
0
 
LVL 1

Author Comment

by:BlearyEye
ID: 33575477
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?
0
 
LVL 4

Expert Comment

by:ricovox
ID: 33576307
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

0
 
LVL 4

Assisted Solution

by:ricovox
ricovox earned 500 total points
ID: 33576399
And as I mentioned before, you will have to find the .Net forms by using the hWnd property.

So here are two functions you might find useful. You could put these directly into the code I sent you.

The first function simply finds a .Net Form given its window Handle (hWnd) that you get from a SystemWindow.

The second function would be useful in your scenario, where you could pass in an array of SystemWIndows (which you get by using SystemWindow.FilterTopLevelWindows with whatever criteria you want..e.g. ThreadID)...and it will either show or hide all of the corresponding Forms (by using the TopMost property...you could just as easily use Opacity or whatever else you want)





private Form FindForm(IntPtr hWnd) {
			foreach (Form f in Application.OpenForms)
				if (f.Handle == hWnd)
					return f;
			return null; //not found
		}
	
		void ShowForm(SystemWindow[] windows, bool show) {
			foreach (SystemWindow win in windows) {
				Form f = FindForm(win.HWnd);
				if (f != null)
					f.TopMost = show;
			}
		}

Open in new window

0
 
LVL 1

Author Comment

by:BlearyEye
ID: 33577266
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.
0
 
LVL 1

Author Comment

by:BlearyEye
ID: 33610521
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.
0
 
LVL 1

Assisted Solution

by:BlearyEye
BlearyEye earned 0 total points
ID: 33794513
Sorry ... I worked on this but somehow neglected to post a follow-up.

I implemented the Managed Windows API approach but the performance was pretty poor ... it took several seconds to build the SystemWindow list - a very noticeable length of time; too long for me to use.
0
 
LVL 4

Expert Comment

by:ricovox
ID: 33992481
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!
0
 
LVL 1

Author Comment

by:BlearyEye
ID: 33993653
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

0
 
LVL 1

Author Comment

by:BlearyEye
ID: 33993679
Here's the screencast.
BlearyEye-363001.flv
0
 
LVL 1

Author Comment

by:BlearyEye
ID: 34075783
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.
0

Featured Post

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
Performance in games development is paramount: every microsecond counts to be able to do everything in less than 33ms (aiming at 16ms). C# foreach statement is one of the worst performance killers, and here I explain why.
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…

856 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question