Thank you Omar for turning me on to Windows Live Writer!!! I've actually managed to reformat my previous blog post and write a new blog post with code snippets using WLW and the Code Snippet plugin for WLW.

Unfortunately, the Paste from Visual Studio plugin wasn't working for me. For some reason, it Community Server doesn't like the PRE and SPAN tags that the plugin used and as a result doesn't format the text properly.

Now I'm actually not dreading the formatting involved in writing another blog post. Still another one to come this week, and it's going to be "the one"... :)

 

There is a very simple requirement of most software that works with multiple forms, which is that there should only be one instance of a form that represents an instance of an object. For example, given a list of Customers, a user should only be able to open one instance of a Customer Form to edit/view a certain instance of a Customer. Once the form is open, selecting the attempting to view the same Customer should simply activate that same form. A new Customer Form will only be created if the existing instance of the form is closed and the Customer is selected again. This will mean that there is a 1 – 0..1 relationship between Customer and Customer Form.

Implementing this is quite simple. One way is to keep a list of open forms and the objects that they represent in a well known place and to only open a new form if one does not already exist in the list. Keeping references to the actual forms, however, will mean that failing to properly clean up the reference will leave the form in memory. This is where Weak References come in to play.

Weak References

Weak References allow the garbage collector to collect an object while still allowing the application to access the object. Weak references are useful for object that use a lot of memory, but can easily be recreated if they are collected by garbage collection - like forms. For more information on weak references, start at MSDN’s Using Weak References and the WeakReference class library.

Window Manager

To accomplish this scenario, I’ve come up with my own class I call Window Manager. Window manager is a generic object that utilizes the singleton pattern to provide all parts of an application access to the forms that represent instances of objects.

The first step to implementing the window manager is to create a generic class that implements the singleton pattern and stores a Dictionary of objects and forms: 

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

As you can see, instead of holding references to the forms themselves, Window Manager holds weak references. Next we need to add methods to add and retrieve form references, so we add the following methods to the class:
public void Add(T businessObject, Form objectForm)
{
    // Subscribe to the form's FormClosing event so we can later remove it from the list.
    objectForm.FormClosing += new FormClosingEventHandler(objectForm_FormClosing);
    // Store a reference to the object and a weak reference to the form in the list.
    WeakReference wr = new WeakReference(objectForm, false);
    wr.Target = objectForm;
    m_WindowList.Add(businessObject, wr);
}
 
public Form this[T businessObject]
{
    get
    {
        Form returnValue = null;
        if (m_WindowList.ContainsKey(businessObject))
        {
            WeakReference weakRef = m_WindowList[businessObject];
            // Check the referenced form is still alive and return a strong reference to it.
            // If the reference is not alive then remove it from the list.
            if (weakRef.IsAlive)
            {
                returnValue = m_WindowList[businessObject].Target as Form;
            }
            else
            {
                m_WindowList.Remove(businessObject);
            }
        }
        return returnValue;
    }
}

Now, we need to ensure that when a form is closed, it is removed from this list so we implement the event handler for the FormClosing event that is created in the Add method.

void objectForm_FormClosing(T sender, FormClosingEventArgs e)
{
    Form window = sender as Form;
    if (window != null)
    {
        T businessObject = null;
        // Find the object that is the key for the form that is closing.
        foreach (KeyValuePair<T, WeakReference> pair in m_WindowList)
        {
            if (pair.Value.Target == window)
            {
                businessObject = pair.Key;
                break;
            }
        }
        // Remove the object from the collection.
        if (businessObject != null)
        {
            m_WindowList.Remove(businessObject);
        }
    }
}

Finally, to start using WindowManager<T> you just have to reference it like in the following code:

private void OpenCustomer(Customer customer)
{
    // Get the instance of the manager and see if it already has this customer.
    WindowManager<Customer> manager = WindowManager<Customer>.Instance;
    CustomerForm customerForm = manager[customer];
    if (customerForm == null)
    {
        // The customer does not have a form related to it, so create one.
        customerForm = new CustomerForm();
        manager.Add(customer, customerForm);
        customerForm.Customer = customer;
        customerForm.MdiParent = this.MdiParent;
        customerForm.Show();
    }
    else
    {
        // Activate the form returned.
        customerForm.Activate();
    }
}

That's it! Now we have a reusable window manager that uses weak references to manage forms in an MDI workspace. The window manager can be used in any part of the application to make sure an object is not already being viewed in another form.