Introduction
Recently in the C# forum, someone asked about how to ensure single instances of certain forms in an MDI application. In my reply, I used generics and an anonymous delegate
to create a slightly more versatile solution to his problem. In his response, he requested an explanation of these topics and perhaps to write (this) an article explaining the code.
The Code
So here are the critical pieces of code in the implementation:
private Dictionary<Type, Form> SingleInstanceForms = new Dictionary<Type, Form>();
protected Form ActivateForm<T>() where T : Form, new()
{
if (!this.SingleInstanceForms.ContainsKey(typeof(T)))
{
T newForm = new T();
newForm.MdiParent = this;
newForm.FormClosed += new FormClosedEventHandler
(delegate(object sender, FormClosedEventArgs e)
{
this.SingleInstanceForms.Remove(sender.GetType());
});
this.SingleInstanceForms.Add(typeof(T), newForm);
}
Form formToActivate = this.SingleInstanceForms[typeof(T)];
formToActivate.Show();
formToActivate.Activate();
return formToActivate;
}
That is, in fact, all the code that is required to enforce single instances of certain forms.
Using the Code
To activate (and if need be, create) the desired form, you simply use the following code:
this.ActivateForm<FormType>();
Explanation of the Code
Dictionary
The dictionary is central to the code, it stores the instances of the forms already displayed in the MDI parent, the key associated with it is the Type
of the form. The dictionary structure automatically provides us with the means to check whether a Form
has been opened previously.
There are advantages and disadvantages to using this. One of the advantages has already been mentioned. The disadvantage is that extra code is required to manage the dictionary, however I feel that this is minor when you contemplate that the only other solution would be to loop through all the existing forms and this will take longer depending on how many forms are open. At the cost of a bit of memory, it is a good solution.
ActivateForm
protected Form ActivateForm<T>() where T : Form, new() { }
This method declaration uses generics to restrict the types being passed to the method with the constraints Form, new()
, i.e. T
must be of type Form
and must have a default constructor.
The method starts of by checking to see if the form's type exists in the dictionary. If not, then it creates a new instance of the form, sets up the necessary properties and adds it to the dictionary.
Anonymous Delegate
The lines of code are as follows:
newForm.FormClosed += new FormClosedEventHandler
(delegate(object sender, FormClosedEventArgs e)
{
this.SingleInstanceForms.Remove(sender.GetType());
});
It could be rewritten as follows:
newForm.FormClosed += new FormClosedEventHandler(this.MdiChild_FormClosed);
private void MdiChild_FormClosed(object sender, EventArgs e)
{
this.SingleInstanceForms.Remove(sender.GetType());
}
There are plenty of good explanations of anonymous delegate
s on the Web, so I won't try and explain those. However the reason the delegate
was used rather than a separate method is that it makes the ActivateForm
method more compact, i.e. in the source code view, rather than the generated IL.
History
- 9th September, 2007: Initial post