MDI Solution Management

  • Thread starter Thread starter Mike
  • Start date Start date
M

Mike

I have a solution with several projects. The first being
the exe and is the MDI Parent form. I have a ton of
properties and methods that I have set up in this form to
do stuff. I have all my MDI child forms in this same
project and my project size is getting way too big. I
would like to break out the child forms into there own
projects (dll's).

Now my problem is that I can add the new project as a
reference to my ParentMDI Form, so that I can work with
the dll, but I can't do the reverse. I need to work with
properties in my MDIParent form and I can't get access to
them from my child form if it is outside of the
ParentForm's project.

I can use this.MDIParent property, but I need to cast it
to my form type I created as my Parent Form. I can't add
a reference to my child mdi project for two reasons:

A) - it complains that you can't add an exe as a
reference it must be dll.
B) - i then tried to change it into a dll, and I get the
error of "Circular Reference" when trying to add this as
a reference. Kind of the chicken and egg scenario here.

I am trying to find the most elegant way to break my
solution up into projects and dll's but still allow my
Main MDI Parent form to talk to the Child Classes and
vise versa.

Any help would be much appreciated.

Thanks
 
Mike,
Move the MDI Parent Interface into its own Project also, via a Separated
Interface Pattern.

http://www.martinfowler.com/eaaCatalog/separatedInterface.html

You should have the following assemblies:
- The Main Executable Assembly
- The MDI Parent Interface Assembly
- Each MDI Child form Assembly

Each MDI child & the Main Executable will reference the MDI Parent Interface
assembly.

The Main Executable will have the MDI Parent Form which implements the MDI
Parent Interface.

The MDI Parent Interface Assembly will have an Interface that the MDI child
can use to talk to the MDI Parent.

In MDI Parent Interface Assembly:
Public Interface IMDIParent
Public Sub DoSomething()
End Interface

In Main Executable Assembly
Public Class MDIParentForm
Inherits System.Windows.Forms.Form
Implements IMDIParent

' other stuff omitted

End Class

You can extend this a step further and have the MDI Parent Interface
Assembly contain a MDI Child Interface also. The MDI Child Interface is the
interface that each child needs to implement so the MDI Parent can talk to
it.

Normally I make the IMDIParent a parameter to the constructors of the MDI
Children...

Hope this helps
Jay
 
Jay, thank you so much for your prompt reply. I have been doing some
reading and was thinking that an Interface is what I might have to do.
I do not have very much experience in c# or .net, so I'm not exactly
sure how to get started with this.

Your message was very concise, I just do not have any experience at all
with Interfaces - developing them nor even what they do.

Does visual studio have tools to make an interface out of an existing
class or anything?

If you have any other sites that might help me get going creating an
Interface that would be great.

I'm assuming the interface know's all about the MDI Parent class, but it
doesn't have any gut's too it... How do you go about developing if this
is somewhat the case - if you add a new method to the MDI Parent class,
do you have to update the interface at the same time?

Thanks again - I'm so lossed on this one, but my main app is getting to
be 5 MB's in size with 100 mdi child forms in it... crazy

Ohh - you mentioned that there should be an assembly for each child mdi
- did you mean that i could do that or have an assembly for several mdi
children that i see deemed to be grouped together? Basically I have
modules and each mdi child represents a table in a database and they are
grouped together to work with each other as forms for inputing and
deleting and stuff.

Thanks
Mike
 
Oh, also Jay wrote:
Mike,
Move the MDI Parent Interface into its own Project also, via a Separated
Interface Pattern.

I don't think I have an MDI Parent Interface yet... I just have the main
exe form that was inherited from System.Windows.Form

I'm not sure if this matters...

Sorry for my ignorance on the subject - I'm going to read about
Interfaces as we speak

Mike
 
Mike,
Does visual studio have tools to make an interface out of an existing
class or anything?
No

You will need to define an Interface.

An interface is like a class, except it has no implementation, on method
signatures (functions, properties & events).

To define an interface use the 'interface' keyword instead of 'class', then
only give member signatures, no member bodies.

public interface IMainForm
{
void DoSomething();
bool DoSomethingElse(int x);
int MyProperty
{
get;
}
}

See the C# Language Specification for details:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfcsharpspec_13.asp


You can use the 'Extract Inteface' refactoring to create the Interface.
http://www.refactoring.com/catalog/extractInterface.html

Basically:
- create a new C# Class Assembly.
- Delete the class1.cs file
- Add a new .cs file that contains the IMainForm interface.
- For each member (function, property or event) in your MainForm that is
being used by a MDI child, add the member's signature to the IMainForm
interface. Only do this for members you added to the form, for members of
Form itself, you don't need them, however you can add them if you want,
especially if it simplifies things in the MDI Children.

- Modify your main form to implement the interface. Seeing as the signature
should be the same, every thing should fall in place.

In your MDI child instead of using the MainForm class, use the IMainForm
interface (they should be interchangeable). Any member that MainForm
inherited you can use the Form class instead.
I'm assuming the interface know's all about the MDI Parent class, but it
doesn't have any gut's too it... How do you go about developing if this
is somewhat the case - if you add a new method to the MDI Parent class,
do you have to update the interface at the same time? Yes

Ohh - you mentioned that there should be an assembly for each child mdi
- did you mean that i could do that or have an assembly for several mdi
children that i see deemed to be grouped together? Basically I have
modules and each mdi child represents a table in a database and they are
grouped together to work with each other as forms for inputing and
deleting and stuff.
You can logically group 2 or more MDI children in to a single Assembly.

Hope this helps
Jay
 
Jay - thanks again, this is great - I think I can take a stab at this
now.

Just some last things to clear up:

1) In my MainForm's Interface representation, you mentioned that I can
only add the methods, properties, events that I need to use in my MDI
Child forms... I hope that is all I have to do, as my main form has a
ton of event handler code and methods to work with a treeview and code
to interact with the child forms. If I only need to put a signature of
the methods and properties that my child forms need, that would be
fairly easy. I'm just not clear if, I need to put all of them or not as
you also mention:
Modify your main form to implement the interface. Seeing >as the signature
should be the same, every thing should fall in place.

so I'm not sure if this matters if my Interface only has methods that I
use in my child forms...

2) I'm not sure how to Implement my Interface in the MainForm... I mean,
I'm not sure how my new Interface class that will live in a new project,
know's about the MainForm class. Does the MainForm class need to
declare something that it uses the IMainForm, or the other way
around....

3)I see that you put a Capital I before the ClassName and keep the class
name the same as a standard. Is there a standard for the project name
that this interface would be built under? The dll name essentially?
Would I create a project called Interfaces and put .cs files of all the
INterfaces I would want to create in it and then have each other dll
reference this Interface dll?

Thank-you so much for your time and help. I have being trying to figure
out away around this for a long time and didn't know anything about
this.

Mike
 
One last thing, you mentioned that you normally pass the IParentMdi
Interface as a parameter to the mdi children's constructor...

What is the purpose of this? Maybe once I get this going, I'll
understand it better.

Thanks
 
Mike,
For forms, the mdi children can use the parent property & cast.

For other types of objects, you need to 'register' the 'parent' with the
child, in this case I would use the constructor.

Hope this helps
Jay
 
Mike:
so I'm not sure if this matters if my Interface only has methods that I
use in my child forms...
If your MDI children only use methods X,Y, & Z, the IMainForm interface only
needs methods X, Y, Z. However your MainForm will need an implementation for
X, Y & Z, but it can also have methods A, B, & C that do other things (take
care of the tree control).
2) I'm not sure how to Implement my Interface in the MainForm...
The MainForm needs to say it implements IMainForm, you do this like you are
inheriting from a class:

Something like:
class MainForm : Windows.System.Forms.Form, IMainForm
{}
3)I see that you put a Capital I before the ClassName and keep the class
name the same as a standard. Is there a standard for the project name
that this interface would be built under? The dll name essentially?
Correct Interfaces normally start with a capital I. The interface should be
named for what it does. For example System.IComparable defines a contract
for comparing objects. System.IFormattable defines a contract for formatting
object. In this case, as it represents the 'interface' of the main form I
called it IMainForm, as I would call the main form MainForm.

The dll itself can be called anything you want. I would call it Interfaces
as you suggest or possible Framework. My projects tend to have a
Framework.dll, which is my class library will all the low level common
'plumbing' stuff such as this interface. Most of the time I name the
assembly for the major namespace in that assembly (System.Windows.Forms.dll
contains the System.Windows.Forms namespace). Note: I include the periods in
the assembly names, just like the framework assemblies.

The "Design Guidelines for Class Library Developers" have all the .NET
standards:
http://msdn.microsoft.com/library/d...ef/html/cpconnetframeworkdesignguidelines.asp

Hope this helps
Jay
 
One other thing that I am concerned with in my design is my mdi children
classes and how I can instantiate them from my MainForm class.

I have a Base Class called TabForm that has most of the properties and
methods I need to talk to my database and do inserts, updates, deletes,
you name it. This BaseClass inherits from form and the only object
added to this form in the designer is a panel.

Now I inherit from this base class on all my Child Mdi Forms and here I
add the text boxes, comboboxes, datetimepickers, etc to the inherited
form and actually they are added to the panel that is in the base class.
My methods for talking to my db are in the base class and my inherited
forms use these methods. They are generally invoked from my Parent MDI
Main Form, from a TreeView Class that is on my MainForm or from a
MainMenu Item.

Currently, I have a HUGE switch statement that decides which MDI Child
Form to load from a backend call that has the name of the class e.g. -
"Organization" - I do a case structure on the word Organization and then
instantiate the child form based off this string. See below:
//this code is in a method
switch (TabFormName.ToUpper())
{
case "ORGANIZATION"
this.iOrganization= new Organization();
this.iOrganization.MdiParent = this;
this.iOrganization.Show();
break;
...
}

I have this huge switch that instantiates the particular class that it
matches by the string. I also as you can see have to declare EVERY
child mdi class globally so that I can refer to it outside of this
method. I am trying to avoid this huge overhead by finding a way to
dynamically call a class name from a string that matches the class name,
but I have had no luck. I found this article, but was unable to
implement it in my solutions:
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&threadm=0957
01c34acc%2453e18be0%24a501280a%40phx.gbl&rnum=1&prev=/groups%3Fhl%3Den%2
6lr%3D%26ie%3DUTF-8%26oe%3Dutf-8%26q%3Dinstantiate%2Bclass%2Busing%2Bstr
ing%2BC%2523%26sa%3DN%26tab%3Dwg

With this interface design I am trying to implement from your guidance -
I am wondering how my MainForm would know about each MDIChild class that
is inherited, as there are close to 100 of these. Currently the child
MDI forms are in the same project as the MDI Parent, so there is no
problem declaring instances of them.

Ikes - I'm opening a can of worms trying to re-engineer this!! But I
love learning!! :)

If you have any suggestions for instantiating a class from a string,
that would be great as well.

Thanks again.
Mike
 
Thank you so much for all your time and info today Jay! This is very
much appreciated, I think I can take a pretty good stab at this.

Mike
 
AWESOME!!!!

I got it to work!!! I have to do quite a bit of reworking some of my
code to extend properties where I was referencing objects in my MainForm
where now I have to do do it through a method or property on my MainForm
and IMainForm.

This is what I changed in my child MDI - it works and seems right, but
just wanted to check

MainForm myMainForm = (MainForm) this.MdiParent;

became

IMainForm myMainForm = (IMainForm) this.MdiParent;

and now I can go myMainForm.CallMyProperties that I have put in the
Interface.

I first tried going IMainForm. - to look for my properties and methods,
but only found two that weren't mine, so I'm assuming I'm doing it
right.

Thank you so much for this information - this is fantastic! I can now
structure my class design a little better and get less load on the main
..exe - we are deploying via a "Smart Client" model off of a web server,
so this will drastically increase load speed and then just use a trickle
down approach to dll's that are needed after the main form is loaded!!

Thanks again,
Mike
 
Mike,
I have this huge switch that instantiates the particular class that it
matches by the string. I also as you can see have to declare EVERY
child mdi class globally so that I can refer to it outside of this
method.
Rather than have a series of global variables in the MainForm for each MDI
Child, Consider making each MDI Child a Singleton via the Singleton pattern.

http://www.yoda.arachsys.com/csharp/singleton.html

Because they are forms, I normally use the First version listed and add
a Closed event handler in the form to clear the instance variable. This
way a new instance of the form is created when first referenced, when the
form is closed this instance is cleared, so that a new instance can be made
the next time the form is shown.

public class Organization : Form
{
static Singleton instance=null;

Organization()
{
}

public static Organization Instance
{
get
{
if (instance==null)
instance = new Organization();
return instance;
}
}

// handle the Closed event to clear instance variable.
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
instance = null;
}

}

The MainForm's assembly would add a reference to this Form and use something
like:
switch (TabFormName.ToUpper())
{
case "ORGANIZATION"
Organization.Instance.MdiParent = this;
Organization.Instance.Show();
break;
...
}

Currently, I have a HUGE switch statement that decides which MDI Child
Form to load from a backend call that has the name of the class e.g. -
"Organization" - I do a case structure on the word Organization and then
instantiate the child form based off this string. See below:

Using code similiar to the following page, you could dynamically load each
MDI child class.

http://www.yoda.arachsys.com/csharp/plugin.html

If I dynamically loaded the MDI Child class I probably would not implement
the Singleton pattern, instead keep the children in a HashTable contained in
the MainForm class.

Hope this helps
Jay
 
Back
Top