How to determine the active form

  • Thread starter Thread starter John Brown
  • Start date Start date
J

John Brown

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;
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,

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.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

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;
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 said:
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;
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.


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;
}
 
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.

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.
 
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;
}

Thanks. Problem solved. See my response to Nicholas.
 
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.
 
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.

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 :)
 
John said:
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
 
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).
 
John said:
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
 
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.
 
John said:
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
 
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.

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.
 
Back
Top