Closing down a hierarchy of modal windows nicely

C

Chukkalove

Im sorry, I dont know the correct description for a hierarchy of parent and
child forms.
I have a main form that opens a child form modally. This child form in turn
opens it's own child form modally.
I also have a separate thread running that monitors a card reader. If the
user removes the card then the thread calls back the main form to tell it
that the card was pulled. The main form should then close down any children
leaving the main form as the only one that's open.
How do i do this cleanly and with as little code as possible please? I'd
prefer not to have to add code to all the forms if possible.
I thought that if I closed the "child" form from the main form, that this
"child" would kill its own "children". This isnt happening. Neither the
"child" or its "children" are closing.
When i used to code in delphi, the owner would always be passed to a child
when the child was constructed, and the child would add itself to its owners
Controls list. I assume this isnt happening here
I also tried changing the constructor for every form so that each form would
add itself to it's owners OwnedForm list. This also didnt work and was very
messy.

thank you
 
D

Dave Sexton

Hi,

When you call the Show method pass in a reference to the owning form (it
will set the Form.Owner property):

Form form = new Form();
form.Show(this);

I've used "this" because I'm assuming that the code above will be executing
within the context of a Form. e.g., an event handler. If not, you'll have
to pass in a reference to the main Form.
 
D

Dave Sexton

Hi,

Sorry, but I didn't realize that you wrote, "modal".

Instead of calling Show(this) call ShowDialog(this).

To close all modal Forms in a chain, each Form must save a reference to the
sub-modal Form that it's showing. Then add the following code to each Form:

protected override void OnClosing(CancelEventArgs e)
{
if (subModalForm != null)
// close the modal Form being shown by this Form
subModalForm.Close();

base.OnClosing(e);
}

To start the chain-reaction and close all open modal dialogs, call Close()
on the first Form.
 
M

Marc Vangrieken

Hi,

I don't think you need to have a reference to subModalForm and you
don't have to call close() on it. When you are using ShowDiaglog(this),
a close() on the first window will do; the children will be closed (and
all their events will be triggered). Or am i missing something?
 
M

Michael C

Dave Sexton said:
Hi Marc,

That doesn't work for modal dialogs.

But you can do something like this:

public void ClosePopups()
{
Form form = FindTopOwner(this);
if(form == null) return;
do
{
form.Close();
form = form.Owner;
if(form == null || form == this) return;
} while(true);
}

private Form FindTopOwner(Form TopForm)
{
Form ownedForm = TopForm;
do
{
if(ownedForm.OwnedForms.Length > 0)
{
ownedForm = ownedForm.OwnedForms[0];
}
else
{
if(ownedForm != TopForm) return ownedForm;
return null;
}
}while(true);
}

Michael
 
M

Michael C

Chukkalove said:
Im sorry, I dont know the correct description for a hierarchy of parent
and child forms.
I have a main form that opens a child form modally. This child form in
turn opens it's own child form modally.
I also have a separate thread running that monitors a card reader.

Out of curiosity does this poll the drive to see if the media has been
removed or is there some API call for this? See my reply to Dave for closing
the forms.

Michael
 
D

Dave Sexton

Hi Michael,

Well, the OP did specify, "How do i do this cleanly and with as little code
as possible please"; I think my solution falls into that category :)

--
Dave Sexton

Michael C said:
Dave Sexton said:
Hi Marc,

That doesn't work for modal dialogs.

But you can do something like this:

public void ClosePopups()
{
Form form = FindTopOwner(this);
if(form == null) return;
do
{
form.Close();
form = form.Owner;
if(form == null || form == this) return;
} while(true);
}

private Form FindTopOwner(Form TopForm)
{
Form ownedForm = TopForm;
do
{
if(ownedForm.OwnedForms.Length > 0)
{
ownedForm = ownedForm.OwnedForms[0];
}
else
{
if(ownedForm != TopForm) return ownedForm;
return null;
}
}while(true);
}

Michael
 
M

Michael C

Dave Sexton said:
Hi Michael,

Well, the OP did specify, "How do i do this cleanly and with as little
code as possible please"; I think my solution falls into that category :)

Not really. The OP did specify that the solution should involve modifying
the popup forms as little as possible. Because you're really duplicating
what's built into windows there's also good potential to introduce bugs.

Michael
 
D

Dave Sexton

Hi Michael,
Not really. The OP did specify that the solution should involve modifying
the popup forms as little as possible. Because you're really duplicating
what's built into windows there's also good potential to introduce bugs.

I'm aware of what the OP wanted, but my solution is very simple and it
certainly isn't "duplicating what's built into windows". Also, it works, so
I'm not sure what type of potential bugs you're considering.

The fact that it requires touching each Form might be too complicated for
the OP, so I'm not going to make any assumptions as to whether the solution
is satisfactory (and I wasn't trying to imply that in my last post either,
if that's what you assumed - note the light-hearted smiley. : ) : )
 
M

Michael C

Dave Sexton said:
I'm aware of what the OP wanted, but my solution is very simple

The simplicity wasn't the issue, more having to modify every form was what I
was commenting on.
and it certainly isn't "duplicating what's built into windows".

Somewhere in windows (or maybe the framework?) each window can store a
reference to an owner form. You are storing a reference to an owner form.
Also, it works, so I'm not sure what type of potential bugs you're
considering.

The same code needs to be duplicated in every form so there is good
potential for an error.
The fact that it requires touching each Form might be too complicated for
the OP, so I'm not going to make any assumptions as to whether the
solution is satisfactory (and I wasn't trying to imply that in my last
post either, if that's what you assumed - note the light-hearted smiley.
: ) : )

I wouldn't use the words "too complicated". I would say that a simpler, more
generic, solution exists so why not use it? My solution was really derived
from posts by Marc and you and is just a more generic version of what you
wrote anyway.

Michael
 
D

Dave Sexton

Hi Michael,

Somewhere in windows (or maybe the framework?) each window can store a
reference to an owner form. You are storing a reference to an owner form.

The Form class has an Owner property. When the Form is a child to another
Form, the Owner property is set, but in the context of "Owner" code, the
simplest way to find that child Form, which needs to be closed, is to assign
the reference to a field when the child is constructed. There is no
"duplication".
The same code needs to be duplicated in every form so there is good
potential for an error.

Given that we're talking about copying only four substantial lines of code,
I don't see any room for error. But if you're worried that the OP might
somehow forget to copy the code to one of the Forms, then inheritance could
fix that if the OP's willing.
I wouldn't use the words "too complicated". I would say that a simpler,
more generic, solution exists so why not use it?

Did I imply that the OP shouldn't?

I think the OP should choose whichever is more appropriate. At this point,
why make any assumptions about requirements that we don't know?

And I don't think your code is simpler at all, BTW. I've glanced at it
twice and I have no idea what it's doing - I'm just assuming that it works
:)
My solution was really derived from posts by Marc and you and is just a
more generic version of what you wrote anyway.

Marc was completely off (no offense Marc :), and I don't see how our
solutions are even close.

My solution is based on the fact that a modal dialog will "buffer" the
FormClosed event until all other owned modals are closed first - that's how
modal Forms behave. So, my code closes the one and only known child modal
(via a field reference) when the Owner itself is being closed, and the
FormClosed events for each modal are raised in a chain-reaction from
top-most to first. It's quite simple, really.
 
M

Michael C

Dave Sexton said:
The Form class has an Owner property. When the Form is a child to another
Form, the Owner property is set, but in the context of "Owner" code, the
simplest way to find that child Form, which needs to be closed, is to
assign the reference to a field when the child is constructed. There is
no "duplication".

I'm not really sure how you consider that not a duplication. You are storing
in a variable that mirrors what the form stores already. The reference is in
2 places. That is a duplication.
Given that we're talking about copying only four substantial lines of
code, I don't see any room for error. But if you're worried that the OP
might somehow forget to copy the code to one of the Forms, then
inheritance could fix that if the OP's willing.

There's always room for error. :)
Did I imply that the OP shouldn't?
Yes.

And I don't think your code is simpler at all, BTW. I've glanced at it
twice and I have no idea what it's doing - I'm just assuming that it works
:)

It is one-off code that can be placed in 1 place in the app. It is more
complex than yours but once it's done it doesn't need to be duplicated in
potentially hundreds of locations. If you're really concerned about the
complexity of mine, the number of lines could easily be reduced.
Marc was completely off (no offense Marc :), and I don't see how our
solutions are even close.

Yours closes each form in many locations, mine does the same thing from one
location. Yours uses a member variable where mine uses the owner property.
My solution is based on the fact that a modal dialog will "buffer" the
FormClosed event until all other owned modals are closed first - that's
how modal Forms behave. So, my code closes the one and only known child
modal (via a field reference) when the Owner itself is being closed, and
the FormClosed events for each modal are raised in a chain-reaction from
top-most to first. It's quite simple, really.

I know how yours works, I used it to write a more generic solution. :)

Michael
 
M

Michael C

Dave Sexton said:
And I don't think your code is simpler at all, BTW. I've glanced at it
twice and I have no idea what it's doing - I'm just assuming that it works
:)

Here's a simplified version of mine. This required 4 lines of code in one
location in the app and does not require code in each form.

private void CloseChildForms(Form ParentForm)
{
foreach(Form form in ParentForm.OwnedForms)
{
CloseChildForms(form);
form.Close();
}
}

Michael
 
D

Dave Sexton

Hi Michael,
I'm not really sure how you consider that not a duplication. You are
storing in a variable that mirrors what the form stores already. The
reference is in 2 places. That is a duplication.

Yes, the variable stores a reference to a child Form which can be found also
by iterating over a collection supplied by the Form itself. You're thinking
much more dynamically about this than I. There is no duplication in storing
a reference that you've already created, which is how I see it:

private Form theReference;
....
theReference = new Form();
theReference.ShowDialog(this);
....
theReference.Close();

No duplication here, from my POV, since the reference was created anyway.
If the OP indicates that each Form may have multiple children then your
solution will probably be better, but that definitely wasn't the case, so
having a "collection" of child Forms vs. a single, known reference are two
very different things - not "duplication".


That's 100% your interpretation because that was not my intention at all.
It is one-off code that can be placed in 1 place in the app. It is more
complex than yours but once it's done it doesn't need to be duplicated in
potentially hundreds of locations. If you're really concerned about the
complexity of mine, the number of lines could easily be reduced.

I was never really concerned at all with your code or its level of
complexity. I've always felt that the OP should choose the best solution,
which may very well have been yours.

But assuming hundreds of modal Forms isn't very realistic. The OP never
said how many modals there are. And since your assuming things, how about
if each of the modals all derive from a common ancestor? In that case my
solution would work quite nicely and probably would be more appropriate than
yours.

And I still don't think either of our solutions even deserve all this
attention :)
Yours closes each form in many locations, mine does the same thing from
one location. Yours uses a member variable where mine uses the owner
property.

They're completely different :)
I know how yours works, I used it to write a more generic solution. :)

The revised code in your adjacent post is much clearer. I'm not sure how my
solution helped you to write it, but if it works - great :)
 
D

Dave Sexton

Hi Michael,

The recursion is much nicer. I assume that the OP will probably find this
code to be more suitable.
 
M

Michael C

Dave Sexton said:
Yes, the variable stores a reference to a child Form which can be found
also by iterating over a collection supplied by the Form itself. You're
thinking much more dynamically about this than I. There is no duplication
in storing a reference that you've already created, which is how I see it:

private Form theReference;
...
theReference = new Form();
theReference.ShowDialog(this);
...
theReference.Close();

I disagree, there are 2 reference to the same thing, hence duplication.
That's 100% your interpretation because that was not my intention at all.

If you wrote "did I say" I would have said no but you wrote "imply" :)
But assuming hundreds of modal Forms isn't very realistic. The OP never
said how many modals there are. And since your assuming things, how about
if each of the modals all derive from a common ancestor? In that case my
solution would work quite nicely and probably would be more appropriate
than yours.

That is possible and might make it easy to extend functionality in the
future.
And I still don't think either of our solutions even deserve all this
attention :)

Very true.
They're completely different :)

Depends really. Both close the modal forms in order. There's very little
code in each so probably 50% is the same. :)
The revised code in your adjacent post is much clearer. I'm not sure how
my solution helped you to write it, but if it works - great :)

Looking at the new code I can't believe how convoluted the previous code was
:)

Michael
 
D

Dave Sexton

Hi Michael,

I'm glad we agree on almost everything...
If you wrote "did I say" I would have said no but you wrote "imply" :)

My intent was not to imply any such thing. The question was actually
rhetorical, but you answered it anyway :)
 
M

Marc Vangrieken

solutions are even close.

Thanks for pointing this out Dave :) I wrote a quick test with modal
forms yesterday, but i made a mistake by issuing a close() on the main
form. And then every modal form does close, as the application goes
down :) :)
 
C

Chukkalove

Thank you for your help all.
I was opening all forms modally using similar to mynewform.ShowDialog(this).
When i tried to close the root form, the child hierarchy didnt close.
This morning my friend, who had been trying to help me last night, sent me
the following code which i placed in my root form.
I think that all child forms need to be shown using ShowDialog(this) rather
than ShowDialog() for it to work but Ive not tested.

This works great and also iterates through all depths

static public void DestroyOwned(Form Root)
{
for ( int d=Root.OwnedForms.GetLength(0) - 1; d >= 0; d--)
{
DestroyOwned(Root.OwnedForms[d]);
Root.OwnedForms[d].Close();
}
}

DestroyOwned(this);// call from the root form
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top