Thursday, September 1, 2011

Dynamically create instances of Forms (or any type)

Problem
I have a form that has a dropdownlist which has "Name of Form", frmObject.

            Dictionary<string, Form> dictForms = new Dictionary<string, Form>(3);
            dictForms .Add("Form 1", new frmOne());
            dictForms .Add("Form 2", new frmTwo());
            dictForms .Add("Form 3", new frmThree());
             comboBoxForms.DataSource = new BindingSource(dictForms , null);
             comboBoxForms.DisplayMember = "Key";
             comboBoxForms.ValueMember = "Value";
             comboBoxForms.SelectedIndex = 0;

Then when the user clicks Open
            Form frm = (Form)comboBoxForms.SelectedValue;
            frm.Show();

The problem with this is after frm.Show(), the frm object is disposed, which means if you try to open the same form it will throw an exception.


Solution
Instead of storing instances of each form object, you can store the type instead, and then use reflection to generate an instance of the form object. This means the form object is created locally, which solves the "disposed of" problem.

            Dictionary<string, Type> dictForms = new Dictionary<string, Type>(3);
            dictForms .Add("Form 1", typeof(frmOne));
            dictForms .Add("Form 2", typeof(frmTwo));
            dictForms .Add("Form 3", typeof(frmThree));
            comboBoxForms.DataSource = new BindingSource(dictForms , null);
            comboBoxForms.DisplayMember = "Key";
            comboBoxForms.ValueMember = "Value";
            comboBoxForms.SelectedIndex = 0;
        }

Then when the user clicks Open
            Form frm = (Form)Activator.CreateInstance((Type)comboBoxForms.SelectedValue);
            frm.Show();

Note:
You can accomplish my original goal by typing in the form name into the combo box, then using a switch to determine which form to open. Doing the way described below means adding a new form only requires 1 line of code to be added, instead of ~5 lines, and also you don't need to sync the names of the forms in the dropdown with the names in the switch

Note 2: 
There's an overload for Activator.CreateInstance  that takes a param array. The solution above will work if all forms use the same parameter or have no parameters at all. It won't work if they require different parameters that have to be assembled right before the call. In other words, if they require a value from the calling form, then this solution will result in needing to check the type first, which defeats the purpose of storing the type.
There was an error in this gadget