.NET Forum / Windows Forms / WinForm General / July 2007
How to determine the active form
|
|
Thread rating:  |
John Brown - 27 Jul 2007 02:32 GMT Hi there,
Does anyone know how to (generically) determine the currently active form for an application using a "static" function (so I can call it from anywhere). There is no offiical way I've been able to find so I've written the following for starters:
public static Form GetActiveForm() { // Returns null for an MDI app Form activeForm = Form.ActiveForm; if (activeForm == null) { FormCollection openForms = Application.OpenForms; for (int i= 0; i < openForms.Count && activeForm == null; ++i) { Form openForm = openForms[i]; if (openForm.IsMdiContainer) { activeForm = openForm.ActiveMdiChild; } } }
return activeForm; }
However, if no MDI child forms are open when this is called then the MDI parent should be returned. What if there's more than one MDI parent in the application however (unlikely but possible I assume). How do you then determine which is active. In any case, the code above seems like a "hack" to me and I'm concerned about any issues I may be neglecting. In fact, the entire situation seems like a major oversight on MSFT's part so I'm uneasy about doing this. If someone can therefore set me on the right track I'd appreciate it. Thanks in advance.
Nicholas Paldino [.NET/C# MVP] - 27 Jul 2007 02:39 GMT John,
I don't think that this is an oversight on MS's part. The ActiveForm property is doing just as it says, it is returning the active form, and null if there isn't one. You are trying to extend the functionality with your own definition, which you can't expect MS to be aware of.
Basically, you don't need to do anything beyond calling ActiveForm, as if it returns null, there is no active form.
You say:
What if there's more than one MDI parent in the application however (unlikely but possible I assume). How do you then determine which is active.
First, it would be HIGHLY unlikely that you would have more than one MDI parent in the app. While I guess it is possible, I can't imagine it being effective, and I wouldn't blame you for making the assumption that case would never happen.
You then ask "how do you determine which is active". If ActiveForm returns null, then no form is active in your app.
 Signature - Nicholas Paldino [.NET/C# MVP] - mvp@spam.guard.caspershouse.com
Hi there,
Does anyone know how to (generically) determine the currently active form for an application using a "static" function (so I can call it from anywhere). There is no offiical way I've been able to find so I've written the following for starters:
public static Form GetActiveForm() { // Returns null for an MDI app Form activeForm = Form.ActiveForm; if (activeForm == null) { FormCollection openForms = Application.OpenForms; for (int i= 0; i < openForms.Count && activeForm == null; ++i) { Form openForm = openForms[i]; if (openForm.IsMdiContainer) { activeForm = openForm.ActiveMdiChild; } } }
return activeForm; }
However, if no MDI child forms are open when this is called then the MDI parent should be returned. What if there's more than one MDI parent in the application however (unlikely but possible I assume). How do you then determine which is active. In any case, the code above seems like a "hack" to me and I'm concerned about any issues I may be neglecting. In fact, the entire situation seems like a major oversight on MSFT's part so I'm uneasy about doing this. If someone can therefore set me on the right track I'd appreciate it. Thanks in advance.
John Brown - 27 Jul 2007 14:49 GMT > I don't think that this is an oversight on MS's part. The ActiveForm > property is doing just as it says, it is returning the active form, and [quoted text clipped - 17 lines] > You then ask "how do you determine which is active". If ActiveForm > returns null, then no form is active in your app. Thanks to both you and Tom I solved the issue. My app is actually an MDI app and the "ActiveForm" property was always returning null so I just assumed this was normal behaviour in an MDI app. That is, I assumed I had to rely on the "ActiveMdiChild" property instead, hence the need for the function in my original post. Your responses seemed to indicate this behaviour isn't normal however so I tried clearing the main form's "IsMdiContainer" property but the "ActiveForm" property was still null. Something wasn't right here obviously. Well it turns out that the "ActiveForm" property wasn't null at all. It's only null if you test it while a breakpoint is active. The VS debugger always returns null for this property IOW but if you assign "ActiveForm" to a variable before hitting the breakpoint, the variable now corrctly shows the main form. Foolish me for not knowing. And shame on MSFT for first stating this was by "design" but not fixing an obvious (and confusing) problem they've known about for 18 months now (see here http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID= 111915). Anyway, thanks again to both of you.
Nicholas Paldino [.NET/C# MVP] - 27 Jul 2007 20:00 GMT John,
I'm sorry, but I got a little chuckle. This is actually by design. When you switch to the debugger, your form is no longer active, so that's why it always returns null.
I mean, that's a basic OS premise, that only one control can have focus at the same time. If you change the ActiveForm property to the way the poster of the bug wanted it, then conceivably, you could have two forms have focus at the same time, which isn't possible.
 Signature - Nicholas Paldino [.NET/C# MVP] - mvp@spam.guard.caspershouse.com
>> I don't think that this is an oversight on MS's part. The ActiveForm >> property is doing just as it says, it is returning the active form, and [quoted text clipped - 35 lines] > http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID= 111915). > Anyway, thanks again to both of you. John Brown - 27 Jul 2007 20:56 GMT > I'm sorry, but I got a little chuckle. This is actually by design. > When you switch to the debugger, your form is no longer active, so that's [quoted text clipped - 4 lines] > poster of the bug wanted it, then conceivably, you could have two forms > have focus at the same time, which isn't possible. I disagree. When a break occurs the debugger should produce information about the state of the application at the moment the break occurred. It's a snapshot of the running program at that moment even if certain restrictions must be implemented to prevent OS conflicts (should someone try to do something illegal with that information while the app is stopped). Moreover, the docs for "Form.ActiveForm" state what should be obvious IMO. It "gets the currently active form for this application". I think that anyone calling this property in the debugger would expect to see exactly that. The fact that it's not technically active while the debugger is running is obvious :)
Peter Duniho - 27 Jul 2007 21:18 GMT >> I mean, that's a basic OS premise, that only one control can have focus >> at the same time. If you change the ActiveForm property to the way the >> poster of the bug wanted it, then conceivably, you could have two forms >> have focus at the same time, which isn't possible. > > I disagree. Yes, but your disagreement seems to have more to do with you simply not liking what's happening than having any good justification for expecting what you want to work.
> When a break occurs the debugger should produce information > about the state of the application at the moment the break occurred. It _does_ do exactly that. The problem is that you are trying to look at the ActiveForm property after the fact. If you saved the ActiveForm value into a local variable before breaking in the debugger, and then inspected the local variable in the debugger after breaking in the debugger, it would have a useful value.
But when you ask for the ActiveForm property after you break in the debugger, it has to run code to do that and that means you are getting the instantaneous value of the property, which at the time you've broken into the debugger is the null value you're seeing.
Surely you do not expect the debugger to run the getter for each and every class property, caching the result just in case you want to look at it while the process is stopped in the debugger? That's the only way the debugger could do what you seem to expect it to do.
> It's a > snapshot of the running program at that moment even if certain restrictions > must be implemented to prevent OS conflicts (should someone try to do > something illegal with that information while the app is stopped). It is a snapshot of the _data_ of the program. But other things, such as the result of code that hasn't been run yet, cannot be put into that "snapshot".
Suppose I had a FileInfo instance, I broke into the debugger, then changed the file from some other process. Should that FileInfo instance be expected to have cached all of the possible property values related to that file when I break into the debugger, just so that the FileInfo instance won't return the current information for the file if and when I ask the debugger to show me those property values?
> Moreover, > the docs for "Form.ActiveForm" state what should be obvious IMO. It "gets > the currently active form for this application". I think that anyone calling > this property in the debugger would expect to see exactly that. The fact > that it's not technically active while the debugger is running is obvious :) Yes, it is obvious. And programming is ALL about the technicalities.
There are a variety of things that can be tricky to debug, when they interact with the UI state of the user. Pretty much anything that has to do with window activation, mouse clicking, redrawing, etc. are things that get messed up when you try to debug them, because the debugger is itself a Windows application that also does all of those things. You need to be aware of this and take it into account when you are debugging.
For what it's worth, if you have access to a second computer, the "right" way to debug this sort of thing is to use remote debugging. That way, the debugger is not running on the same computer as the process being debugged and it won't interfere with the debugged process.
If you don't, then you can do things like using the Debug.WriteLine() method to log information, or even to do as I suggested above and save interesting values into local variables so that you can then inspect them in the debugger without requiring code to run, and thus without having the actual state of interest being affected by the debugger.
Pete
John Brown - 27 Jul 2007 23:10 GMT > Yes, but your disagreement seems to have more to do with you simply not > liking what's happening than having any good justification for expecting > what you want to work. I don't pretend the situation is always black and white nor do I profess to a have complete understanding of all the issues since I don't work on debuggers for a living. I'm sure you (probably) don't either. I just didn't start programming yesterday however. I've been in the coding trenches longer than most and I know the situation isn't always cut-and-dry. There are costs to pay sometimes and they may be prohibitively expensive. This is a deep area after all. What I'm expecting here however is completely legitimate and correct even if someone demonstrates that it's too (intrinsically) expensive to provide. That argument I can accept. What I don't accept is someone telling me I'm being unjustified and that this has more to do with me "not liking what's happening". Expecting a debugger to return this type of information is not only justified, it's what anyone would naturally and reasonably expect. The irony is that after many years of programming in the C++ world I don't recall ever having the types of debugging problems that seem to be commonplace in the .NET world (including very serious issues trying to debug threads in a form-based application but that's another story).
Peter Duniho - 28 Jul 2007 00:56 GMT > I don't pretend the situation is always black and white nor do I profess to > a have complete understanding of all the issues since I don't work on > debuggers for a living. I'm sure you (probably) don't either. You don't need to work on debuggers for a living to understand the difference between the debugger showing you static information and having to run code to evaluate something.
You are asking it to do the latter, and so it should be obvious that the result is going to reflect the _current_ state, not some state in recent history (such as when you interrupted the process with the debugger).
I just didn't
> start programming yesterday however. I've been in the coding trenches longer > than most and I know the situation isn't always cut-and-dry. How long you've "been in the trenches" is irrelevant. I do note that many people, when they run out of factual things on which to base their arguments, resort to that sort of comment though. They are essentially saying "I have more experience than you, so you should just believe me".
In my own experience, those people are usually wrong about both parts of that claim, though I admit that every once in a great while someone comes along who can at least make claim to the first.
There are costs
> to pay sometimes and they may be prohibitively expensive. This is a deep > area after all. What I'm expecting here however is completely legitimate and > correct even if someone demonstrates that it's too (intrinsically) expensive > to provide. I don't know how you are defining "completely legitimate", but by my definition what you want is definitely _not_ "completely legitimate".
It's not that what you are asking for is simply "too expensive to provide". It is, but that phrase doesn't really convey the impracticality of what you think is the correct behavior.
You are looking at just one property. But for the debugger to behave in a consistent way, if the debugger is going to give you what you want, it must evaluate _EVERY_ possible function call and cache the results of that. Even if you want to limit the behavior to properties (not logical, IMHO, but hey...let's give you the benefit of the doubt), the debugger would still need to evaluate _EVERY_ possible property of _EVERY_ class instantiated, along with _EVERY_ static property of every class that is referenced in the application.
It is absurd to think that that's somehow a desirable thing to have the debugger do in the general case. Even ignoring for the moment the riskiness of calling code arbitrarily every time the debugger interrupts the process, the number of function calls required to do that, and the amount of memory required to store all of the results, is not only "expensive", it's simply prohibitive.
And before you start typing trying to debate the above, you need to consider what a property really is: it's just a method that returns a value. The value could come from anywhere; it's not necessarily just a matter of retrieving something that's already in memory, doing so could at the very least require allocation of an arbitrarily large data structure, as well as the execution of code with any number of undesirable side-effects (yes, properties should IMHO be free of side-effects but there is no guarantee that they are)
And addressing manually specific cases such as yours is trivial, without changing the way debuggers work today.
That argument I can accept. What I don't accept is someone
> telling me I'm being unjustified and that this has more to do with me "not > liking what's happening". I wrote that because it seems to me that anyone who stopped to think about the implications of what they are demanding would quickly realize how impossible the demand is. That leaves me with the conclusion that you must NOT have thought about the implications and thus are basing your demands simply on what you want, rather than what can actually be done.
Expecting a debugger to return this type of
> information is not only justified, it's what anyone would naturally and > reasonably expect. No, it's not "what anyone would naturally and reasonably expect". A simple Google search would reveal to you a relative lack of complaints similar to you (as in, practically none), in spite of the relatively large number of people writing Windows code.
Can you point to a single commonly used debugger that supports the behavior you are expecting?
The irony is that after many years of programming in the
> C++ world I don't recall ever having the types of debugging problems that > seem to be commonplace in the .NET world (including very serious issues > trying to debug threads in a form-based application but that's another > story). For you to feel that this is a serious debugging problem, and in particular that it's somehow unique to .NET, suggests to me that you do not in fact have very much experience writing GUI code, especially under Windows (though this exact issue does exist on other platforms).
And frankly, these issues aren't really even all that unique to a GUI. On a text-only platform, you run into the same sort of thing, if your code happens to change or query the state of the output device and the same output device is used both for program output and debugging. They are perhaps a bit more pronounced on a GUI, but they are not unique to a GUI, and they are _certainly_ not unique to .NET (one extremely common example is trying to debug a redraw event handler -- WM_PAINT, OnPaint, whatever -- where the mere act of having the debugger interrupt the code in the redraw handler causes the invalidation of the thing being redrawn, forcing another redraw event immediately after finishing the current one).
Let me be clear: this is not a .NET problem. You would have the same issue if you were writing native Win32 code and calling GetActiveWindow() or GetFocus() (which are the equivalents of the ActiveForm property).
Pete
John Brown - 28 Jul 2007 02:48 GMT You're obviously very motivated to prove your point to me. This was never.a debate however and you're not in possession of all the facts. I seriously doubt you would concede on any issue even if I took the time to show you detailed evidence contradicting some of the things you've said (from MSFT itself no less). I have no inclination to carry on with this however. You have your views and I have mine.
Peter Duniho - 28 Jul 2007 04:59 GMT > You're obviously very motivated to prove your point to me. I am motivated to make sure that truthful, factual things are written, and that people who have unrealistic expectations are disavowed of those expectation. Especially when they carry with them a "so-and-so sucks because they don't do what I want them to" attitude.
This was never.a
> debate however and you're not in possession of all the facts. I seriously > doubt you would concede on any issue even if I took the time to show you > detailed evidence contradicting some of the things you've said (from MSFT > itself no less). Well, you are wrong about that. I have never claimed to be 100% correct, and I'm one of the few people who post to this newsgroup (or any for that matter) who has ever admitted to be wrong. Your doubts are founded only in your own prejudice, and not of any factual basis.
So, if you have facts that support your contention, please feel free to post. I doubt you do, but do feel free to post what you think supports your claim that the debugger should evaluate each and every property that you might look at while the process is interrupted.
Saying you have facts without providing them is useless, and a fairly common tactic among people who actually don't have facts. It's easy to _say_ you can support your claim; it's much more difficult to actually do it.
I have no inclination to carry on with this however. You
> have your views and I have mine. It's not a matter of opinion (or of one's "view", as you put it). There's just not any feasible way a debugger could do what you want it to do.
Pete
Jack Jackson - 28 Jul 2007 07:06 GMT >John, > [quoted text clipped - 6 lines] >poster of the bug wanted it, then conceivably, you could have two forms have >focus at the same time, which isn't possible. While you can argue that it is acting correctly (ActiveForm is set to null when no form in the application is the current Windows form), I think it is not desirable behavior and needs to be documented in the ActiveForm property.
A debugger should affect the target as little as possible. Known situations where the debugger affects the target should be documented. In this case it might not be feasible to change this behavior since it is probably caused by the underlying OS behavior.
I'm sure I would have been burned by this too.
Tom Porterfield - 27 Jul 2007 03:30 GMT > Hi there, > [quoted text clipped - 21 lines] > > However, if no MDI child forms are open when this is called then the MDI parent should be returned. What if there's more than one MDI parent in the application however (unlikely but possible I assume). How do you then determine which is active. In any case, the code above seems like a "hack" to me and I'm concerned about any issues I may be neglecting. In fact, the entire situation seems like a major oversight on MSFT's part so I'm uneasy about doing this. If someone can therefore set me on the right track I'd appreciate it. Thanks in advance. Your comment about returning null if it is an MDI app confuses me as that is not the behavior I have noticed. In an MDI app the active form will usually be the MDI container unless a dialog is being displayed. If you want that, then that's how you get it. If you want the active child then test to see if the ActiveForm is an MDI container and if so call ActiveMdiChild. You should only need to do the following:
Form activeForm = Form.ActiveForm;
if (activeForm.IsMdiContainer && activeForm.ActiveMdiChild != null) { activeForm = activeForm.ActiveMdiChild; }
 Signature Tom Porterfield
John Brown - 27 Jul 2007 15:12 GMT > Your comment about returning null if it is an MDI app confuses me as that > is not the behavior I have noticed. In an MDI app the active form will [quoted text clipped - 9 lines] > activeForm = activeForm.ActiveMdiChild; > } Thanks. Problem solved. See my response to Nicholas.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|