2010-08-13 5 views

risposta

4

È necessario utilizzare la riflessione.

È possibile ottenere un riferimento a ciascuna proprietà nel controllo sorgente (in base al tipo), quindi "ottenere" il suo valore, assegnando tale valore al controllo target.

Ecco un esempio di greggio:

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); 
      } 
     } 
    } 
+2

Nota il commento sotto la risposta di CodeSawyGeek - questo codice copia ciecamente ogni proprietà. Potrebbe essere pericoloso. –

+1

Hm, controlli un metodo set ma non un metodo get (anche se ammetto che le proprietà di sola scrittura sono rare). Ma tieni presente che il tuo codice copierà proprietà come 'Parent',' Name' e 'Location' che potrebbero non essere desiderati. – Timwi

+0

L'uso del riflesso in questo modo probabilmente non ti darà i risultati desiderati. Un DataGridView è un oggetto molto complesso e potresti non essere in grado di copiarlo completamente (o correttamente) copiando alla cieca i suoi valori di proprietà. –

2

Si potrebbe utilizzare la reflection per ottenere tutte le proprietà pubbliche del tipo e copiare i valori da un'istanza all'altra, ma questo è pericoloso e potrebbe non duplicare realmente l'intero stato dell'oggetto. Potrebbero esserci alcune proprietà che non desideri copiare (ad es. Genitore, sito) e altre proprietà importanti che non puoi impostare direttamente (ad es. Colonne, righe). Inoltre, potrebbero esserci proprietà che sono tipi di riferimento; il tuo controllo copiato finirebbe per riferirsi allo stesso oggetto dell'originale, il che potrebbe non essere desiderabile. Potrebbero esserci anche informazioni di stato che possono essere impostate solo tramite chiamate di metodo, che non verranno copiate in questo modo. In breve, la riflessione probabilmente non è la soluzione che stai cercando.

Potrebbe essere sufficiente copiare manualmente le proprietà desiderate. In alternativa, è possibile creare un metodo factory in grado di creare un numero qualsiasi di griglie simili.

+1

... Tuttavia, è necessario essere attento perché se lo fai ciecamente, finirai per copiare proprietà come 'Parent',' Name' e 'Location' che potresti non volere. – Timwi

+0

@Timwi: ho modificato la mia risposta per evidenziare i pericoli dell'utilizzo del riflesso in questo modo. –

+0

+1 Per il metodo factory, anche se probabilmente sarebbe più probabile che si configuri il controllo già istanziato, quindi non è vero factory, ma la stessa idea ed è il modo in cui potrei farlo. – andyhasit

0

ho usato questo codice per clonare controllo, perché voglio copiare solo le proprietà selezionate.

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

Ecco il codice che mi è venuto in mente. L'ho provato solo con i controlli Label, TextBox, Panel e DataGridView. Per un controllo Panel otterrai tutti i controlli contenuti (istanze clonate). Per un controllo DataGridView si otterrà il binding dei dati e saranno gli stessi dati esatti associati al controllo DataGridView di origine. Naturalmente, se non c'è un legame, l'istanza clonata non avrà alcun legame. Se questi comportamenti siano desiderabili o meno dipende dalle tue esigenze.

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); 
} 
+0

'System.ComponentModel.DefaultValueAttribute' e' System.ComponentModel.CategoryAttribute' sono altri 2 da verificare; se sono presenti, la proprietà è destinata a comparire nel designer. Molte proprietà di DGV non hanno browsable o editor browsable, ma hanno uno o entrambi, ad es. le proprietà 'AllowUserTo *'. –

0

Sulla base this post qui è una versione che

  • crea i tipi di controllo corretti e
  • lo fa in modo ricorsivo

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

È ancora possibile che si desideri prova se più di th La proprietà e WindowTarget deve essere filtrata.

divertente a parte: Se il controllo di clone è (a) un non selezionatoTabPage sarà invisibile ..

0

ho usato questo:

Control NewControl=new Control(ControlToClone,ControlToClone.Name); 
+0

Cloud spieghi un po 'la tua soluzione. Sembra ok ma senza ulteriori spiegazioni la risposta potrebbe essere cancellata a causa della scarsa qualità. Aiuta gli altri visitatori a capire la risposta e fornire i motivi per utilizzarla. –

Problemi correlati