8

For example, I have a DataGridView control with a Blue BackgroundColor property etc.., is there a way which I can transfer or pass programatically these properties to another DataGridView control?

Something like this:

dtGrid2.Property = dtGrid1.Property; // but of course, this code is not working

Thanks...

Kevin Kibler
  • 13,357
  • 8
  • 38
  • 61
yonan2236
  • 13,371
  • 33
  • 95
  • 141

8 Answers8

8

You'll need to use reflection.

You grab a reference to each property in your source control (based on its type), then "get" its value - assigning that value to your target control.

Here's a crude example:

    private void copyControl(Control sourceControl, Control targetControl)
    {
        // make sure these are the same
        if (sourceControl.GetType() != targetControl.GetType())
        {
            throw new Exception("Incorrect control types");
        }

        foreach (PropertyInfo sourceProperty in sourceControl.GetType().GetProperties())
        {
            object newValue = sourceProperty.GetValue(sourceControl, null);

            MethodInfo mi = sourceProperty.GetSetMethod(true);
            if (mi != null)
            {
                sourceProperty.SetValue(targetControl, newValue, null);
            }
        }
    }
Stuart Helwig
  • 9,318
  • 8
  • 51
  • 67
  • 3
    Note the comment under CodeSawyGeek's answer - this code blindly copies every property. Could be dangerous. – Stuart Helwig Aug 13 '10 at 03:40
  • 1
    Hm, you check for a set method but not for a get method (although I admit write-only properties are rare). But note that your code would copy properties like `Parent`, `Name` and `Location` which may not be desired. – Timwi Aug 13 '10 at 03:42
  • Using reflection this way is probably not going to give you the results you want. A DataGridView is a very complex object, and you might not be able to completely (or correctly) copy it by blindly copying its property values. – Kevin Kibler Aug 13 '10 at 04:06
3

You could use reflection to get all the public properties of the type and copy the values from one instance to another, but this is dangerous and might not really duplicate the entire state of the object. There might be some properties that you don't want to copy (e.g. Parent, Site), and other important properties that you can't set directly (e.g. Columns, Rows). Also, there could be properties that are reference types; your copied control would end up referencing the same object as your original, which could be undesirable. There could also be state information that can only be set through method calls, which won't be copied this way. In short, reflection probably isn't the solution you're looking for.

You may just have to manually copy the properties you want. Alternatively, you could create a factory method that can create any number of similar grids.

Kevin Kibler
  • 13,357
  • 8
  • 38
  • 61
  • 1
    ... However, you need to be careful because if you do it blindly, you would end up copying properties like `Parent`, `Name` and `Location` which you might not want. – Timwi Aug 13 '10 at 03:39
  • @Timwi: I modified my answer to point out the dangers of using reflection this way. – Kevin Kibler Aug 13 '10 at 04:03
  • +1 For factory method, although would probably be more likely to be configuring the already instantiated control so not true factory, but same idea and it's the way I'd go about it. – andyhasit Feb 18 '14 at 15:04
2

I posted a demo project on codeproject on how to copy&paste or clone a contorl a few years ago, http://www.codeproject.com/Articles/12976/How-to-Clone-Serialize-Copy-Paste-a-Windows-Forms

lxwde
  • 119
  • 3
1

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);
}
TxCsharper
  • 616
  • 7
  • 6
  • `System.ComponentModel.DefaultValueAttribute` and `System.ComponentModel.CategoryAttribute` are 2 others to check; if they are present, the property is meant to show up in the designer. Many of DGV's properties don't have browsable or editor browsable, but have one or both of these, e.g. the `AllowUserTo*` properties. – Matt Chambers Feb 07 '18 at 00:27
0

I Used below code to copy selected properties.

public static void CloneControl(Control SourceControl, Control DestinationControl)
{
    String[] PropertiesToClone = new String[] { "Size", "Font", "Text", "Tag", "BackColor", "BorderStyle", "TextAlign", "Width", "Margin" };
    PropertyInfo[] controlProperties = SourceControl.GetType().GetProperties();

    foreach (String Property in PropertiesToClone)
    {
        PropertyInfo ObjPropertyInfo = controlProperties.First(a => a.Name == Property);
        ObjPropertyInfo.SetValue(DestinationControl, ObjPropertyInfo.GetValue(SourceControl));
    }
}
Manish Nayak
  • 635
  • 9
  • 18
0

Based on this post here is a version that

  • creates the correct control types and
  • does so recursively

public static class ControlExtensions
{
    public static T Clone<T>(this T controlToClone)  where T : Control
    {
        PropertyInfo[] controlProperties = 
          typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        //T instance = Activator.CreateInstance<T>();
        Control instance = (Control) Activator.CreateInstance(controlToClone.GetType());

        foreach (PropertyInfo propInfo in controlProperties)
        {
            if (propInfo.CanWrite)
            {
                if (propInfo.Name != "WindowTarget")
                    propInfo.SetValue(instance,
                                      propInfo.GetValue(controlToClone, null), null);
            }
        }

        foreach(Control ctl in controlToClone.Controls)
        {
            instance.Controls.Add( ctl.Clone() );
        }
        return (T) instance;
    }
}

You still may want to test if more than the WindowTarget property should be filtered out..

Funny aside: If the control to clone is (on) an unselected TabPage it will be invisible..

TaW
  • 53,122
  • 8
  • 69
  • 111
0

i used this:

 Control NewControl=new Control(ControlToClone,ControlToClone.Name);
elle0087
  • 840
  • 9
  • 23
  • Cloud you explain your solution a little bit. It looks ok but without further explanation the answer may be deleted due to low-quality. Help fellow visitors to understand the answer and provide reasons for using it. – Bruno Bieri Oct 24 '17 at 13:43
0

After having set all the properties in the Designer I then wanted to copy all this configurations to another DataGridView and I faced the same problem. I solved it inspecting the InitializeComponent(); part of the form. There was easy to find all the properies that I had set and to set it again by code to another DataGridView control.

For example, I had set:

DataGridViewCellStyle dgvCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
dgvCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dgvCellStyle1.BackColor = System.Drawing.SystemColors.Window;
dgvCellStyle1.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dgvCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText;
dgvCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dgvCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dgvCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
dgv.DefaultCellStyle = dgvCellStyle1;
dgv.GridColor = System.Drawing.SystemColors.ControlLight;
dgv.Location = new System.Drawing.Point(258, 315);                    
dgv.ReadOnly = true;
dgv.RowHeadersVisible = false;
dgv.RowTemplate.Height = 18;
dgv.ShowEditingIcon = false;      
MundoPeter
  • 704
  • 6
  • 12