In response to a question asked by David on a previous post Using Weak References to Manage Forms in an MDI Workspace, I've decided to have put together a short write up on a way of implementing the same behavior for search forms.
The question asked by David was:
How would the window manager track a form that is not representing a business object? For instance, the MDI application I am maintaining has search forms that shows grids of results, which may (or may not) still be opened, and if the user selects one of these same search forms from the menus/toolbars, that form should be activated.
Personally, I don't think the window manager would be the best solution to this problem. What David is trying to accomplish might be simpler to do by implementing the singleton pattern on the search forms he is developing.
The singleton pattern is one of the simplest and most used patterns today. It allows you to have only one instance of an object. Generally, a singleton is implemented by making the constructor a private method and exposing a method or property to get an instance of the object. An example of this can be seen in the window manager class:
public class WindowManager<T>
{ private Dictionary<T, WeakReference> m_WindowList;
private static WindowManager m_WindowManager;
private WindowManager()
{ m_WindowList= new Dictionary<T, WeakReference>();
}
public static WindowManager Instance
{ get
{ if (m_WindowManager == null)
{ m_WindowManager = new WindowManager();
}
return m_WindowManager;
}
}
}
Put simply, the Instance property allows you to get an Instance of the object by instantiating a static instance of the class and holding a reference to that instance. The constructor is hidden to enforce its usage.
However, this can't exactly be done on a Windows forms because the constructor must be public in order for the designer to be able to paint the screen. You can of course implement the same pattern on a form, but leave the constructor as public. This means that the developers must be careful when they are creating the form, as instantiating a new form and showing it in the MDI container will not take any notice of the state of the internal reference.
So, how would this look...?
public class CustomerSearchForm : Form
{ private static CustomerSearchForm m_CustomerSearchForm;
public CustomerSearchForm()
{ InitializeComponent();
}
public static CustomerSearchForm SingletonInstance
{ get
{ if (m_CustomerSearchForm == null)
{ m_CustomerSearchForm = new CustomerSearchForm();
}
return m_CustomerSearchForm;
}
}
}
And to call it from a shell form when a menu item is clicked would look something like this...
public class MainForm : Form
{ ...
private void mnuCustomerSearch_Click(object sender, EventArgs e)
{ CustomerSearchForm searchForm = CustomerSearchForm.SingletonInstance;
if (!searchForm.Visible)
{ searchForm.MdiParent = this;
searchForm.Show();
}
else
{ searchForm.Activate();
}
}
}
Another simpler way of achieving the same result would be to iterate through the MDI container's children until you find a Form of the same type as the form you want to open.
private void mnuCustomerSearch_Click(object sender, EventArgs e)
{ CustomerSearchForm searchForm = null;
foreach (Form childForm in this.MdiChildren)
{ searchForm = childForm as CustomerSearchForm;
if (searchForm != null) break;
}
if (searchForm != null)
{ searchForm.Activate();
}
else
{ searchForm = new CustomerSearchForm();
searchForm.MdiParent = this;
searchForm.Show();
}
}
This would achieve the same result and would probably be easier to for developers with less experience in design patterns.
Hope this helps you David. :)