Here's the code that I came up with. I've only tested it with the Label, TextBox, Panel, and DataGridView controls. For a Panel control you will get all the contained controls (cloned instances). For a DataGridView control you will get the data binding and it will be the exact same data that is bound to the source DataGridView control. Of course, if there is not binding then the cloned instance will have no binding. Whether these behaviors are desirable or not depends on your needs.
private Control CloneControl(Control srcCtl)
{
var cloned = Activator.CreateInstance(srcCtl.GetType()) as Control;
var binding = BindingFlags.Public | BindingFlags.Instance;
foreach(PropertyInfo prop in srcCtl.GetType().GetProperties(binding))
{
if (IsClonable(prop))
{
object val = prop.GetValue(srcCtl);
prop.SetValue(cloned, val, null);
}
}
foreach(Control ctl in srcCtl.Controls)
{
cloned.Controls.Add(CloneControl(ctl));
}
return cloned;
}
private bool IsClonable(PropertyInfo prop)
{
var browsableAttr = prop.GetCustomAttribute(typeof(BrowsableAttribute), true) as BrowsableAttribute;
var editorBrowsableAttr = prop.GetCustomAttribute(typeof(EditorBrowsableAttribute), true) as EditorBrowsableAttribute;
return prop.CanWrite
&& (browsableAttr == null || browsableAttr.Browsable == true)
&& (editorBrowsableAttr == null || editorBrowsableAttr.State != EditorBrowsableState.Advanced);
}