如何克隆或序列化Windows窗体控件?
当使用此代码“CloneControl(控制CT1)”我试图克隆Windows窗体控件,它可以让我复制控制一些序列化的性质,不是所有属性。
public Form1()
{
InitializeComponent();
Columns = new DataGridViewTextBoxColumn[2];
for (int i = 0; i < 2; i++)
{
Columns[i] = new System.Windows.Forms.DataGridViewTextBoxColumn();
//
// Columns[i]
//
Columns[i].HeaderText = "j" + (i + 1);
Columns[i].Name = "Column" + (i + 1);
Columns[i].Width = 50;
}
dataGridView1 = new System.Windows.Forms.DataGridView();
dataGridView1.Name = "dataGridView1";
dataGridView1.Location = new System.Drawing.Point(100, 100);
dataGridView1.RowHeadersWidth = 50;
dataGridView1.RowTemplate.Height = 25;
dataGridView1.Size = new System.Drawing.Size(55 + 50 * 2, 25 + dataGridView1.RowTemplate.Height * 2);
dataGridView1.Anchor = System.Windows.Forms.AnchorStyles.None;
dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView1.Columns.AddRange(Columns);
dataGridView1.TabIndex = 3;
dataGridView1.AllowUserToAddRows = false;
dataGridView1.Rows.Add();
dataGridView1.Rows.Add();
dataGridView1.Rows[0].HeaderCell.Value = "i" + 1;
dataGridView1.Rows[1].HeaderCell.Value = "i" + 2;
dataGridView1.Rows[0].Cells[0].Value = "value1";
Controls.Add(dataGridView1);
Control cloned1 = CloneControl(dataGridView1);
cloned1.SetBounds(cloned1.Location.X, cloned1.Location.Y + 300, cloned1.Width, ct1.Height);
Controls.Add(cloned1);
cloned1.Show();
}
public Control CloneControl(Control ct1)
{
Hashtable PropertyList = new Hashtable();
PropertyDescriptorCollection Properties = TypeDescriptor.GetProperties(ct1);
Assembly controlAsm = Assembly.LoadWithPartialName(ct1.GetType().Namespace);
Type controlType = controlAsm.GetType(ct1.GetType().Namespace + "." + ct1.GetType().Name);
Control cloned1 = (Control)Activator.CreateInstance(controlType);
foreach (PropertyDescriptor pr1 in Properties)
{
if (pr1.PropertyType.IsSerializable)
{
PropertyList.Add(pr1.Name, pr1.GetValue(ct1));
}
if (PropertyList.Contains(pr1.Name))
{
try
{
Object obj = PropertyList[pr1.Name];
pr1.SetValue(cloned1, obj);
}
catch (Exception ex)
{
}
}
}
return ct2;
}
如果您运行的代码......在您将得到
正如你可以在main方法见我创建dataGridView1,其中有一些属性的克隆。
而实际上每个单元格的值是一个克隆的dataGridView空。 另外一个列的大小,不会克隆!
你可能有一个问题:如果这是写在C#Visual Studio或SharpDeveloper作为IDE可以处理这个问题,那么有可能写那种代码! 对?
在Visual Studio当你试图拖放控件,或者复制与所有属性控制糊控制,它不仅复制(包括序列化或非序列化),而且它改变控件本身从“dataGridView1”到“名dataGridView2" 以及在SharpDeveloper!
我该怎么办?
我应该建立什么样的方法?
也许另一个控制有许多非序列化的属性!
如何复制所有的人?
请任何人.....
就像在评论中提到@Hans, Clone
并不容易。 如果你想获得一些相同的控制,只有一点不同,你最好用的功能来定义的一般行为 和作为参数传递不同的属性 。 例如,我们定义与适用的DataGridView一些通用性的功能:
private void InitDataGridView(DataGridView dataGridView, string name)
{
dataGridView.Name = name;
// configure other properties here
dataGridView.Location = new System.Drawing.Point(100, 100);
dataGridView.RowHeadersWidth = 50;
dataGridView.RowTemplate.Height = 25;
dataGridView.Size = new System.Drawing.Size(55 + 50 * 2, 25 + dataGridView1.RowTemplate.Height * 2);
dataGridView.Anchor = System.Windows.Forms.AnchorStyles.None;
dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
// remember to initialize your columns, or pass it in as a parameter
dataGridView.Columns.AddRange(Columns);
dataGridView.AllowUserToAddRows = false;
dataGridView.Rows.Add();
dataGridView.Rows.Add();
dataGridView.Rows[0].HeaderCell.Value = "i" + 1;
dataGridView.Rows[1].HeaderCell.Value = "i" + 2;
dataGridView.Rows[0].Cells[0].Value = "value1";
}
public Form1()
{
InitializeComponent();
var dataGridView1 = new DataGridView();
var dataGridView2 = new DataGridView();
InitDataGridView(dataGridView1, "dataGridView1");
InitDataGridView(dataGridView2, "dataGridView2");
}
IDE(如Visual Studio中)正在使用PropertyDescriptors
, DesignerSerializationVisibility
和ShouldSerializeValue
, 但数据网格行有一些特别的东西,因为你不能在设计时将它们添加! IDE无法复制的东西是不存在的,因此,解决方案必须是不同的(如果你想克隆超出了IDE / Designer可以做到控制-看其他答案和注释本)。 试试我的代码(除网格行的一切得到了克隆没有额外的检查- 列得到了克隆)。
foreach(PropertyDescriptor pd in TypeDescriptor.GetProperties(src)) {
if(!pd.ShouldSerializeValue(src)) {
if(src is DataGridView && pd.Name == "Rows")
CopyDataGridRows((DataGridView)src, (DataGridView)dst);
continue; }
注:以上可以(为类末支票)来完成好,但就是因为它是很明显。
using System;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace CloneControls {
public partial class Form1: Form {
public Form1() { InitializeComponent(); }
private void Form1_Load(object sender, EventArgs e) {
dataGridView1.Rows.Add();
dataGridView1.Rows.Add();
foreach(Control c in splitContainer1.Panel1.Controls)
splitContainer1.Panel2.Controls.Add((Control)Clone(c));
}
static object Clone(object o) {
return Copy(o, Activator.CreateInstance(o.GetType()));
}
static object Copy(object src, object dst) {
IList list = src as IList;
if(list != null) {
IList to = dst as IList;
foreach(var x in list)
to.Add(Clone(x));
return dst; }
foreach(PropertyDescriptor pd in TypeDescriptor.GetProperties(src)) {
if(!pd.ShouldSerializeValue(src)) {
if(src is DataGridView && pd.Name == "Rows")
CopyDataGridRows((DataGridView)src, (DataGridView)dst);
continue; }
switch(pd.SerializationVisibility) {
default: continue;
case DesignerSerializationVisibility.Visible:
if(pd.IsReadOnly) continue;
pd.SetValue(dst, pd.GetValue(src));
continue;
case DesignerSerializationVisibility.Content:
Copy(pd.GetValue(src), pd.GetValue(dst));
continue;
}
}
return dst;
}
static void CopyDataGridRows(DataGridView src, DataGridView dst) {
foreach(DataGridViewRow row in src.Rows)
if(!row.IsNewRow) dst.Rows.Add((DataGridViewRow)Clone(row));
}
}
}
我想我在这里做更多更好的方法。
此法在财产首先检查接口:如果是的ICollection那么它的第一份工作。
在此之后一个循环的方法“DeepClone()”结尾,则有必要做另一个循环,不检查属性类型接口......我的意思是我不能混用这两种操作为一个循环?
你也可以检测到会有某种运行时异常的,为此我把这个代码放到try-catch块...
Control cloned1 = (Control)DeepClone(dataGridView1);
cloned1.SetBounds(cloned1.Location.X, cloned1.Location.Y + 300, cloned1.Width, ct1.Height);
Controls.Add(cloned1);
cloned1.Show();
public dynamic DeepClone(dynamic ob1)
{
dynamic ob2 = null;
if (ob1.GetType().IsSerializable && !ob1.GetType().IsArray)
{
if (ob1 != null)
{
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, ob1);
ms.Position = 0;
ob2 = formatter.Deserialize(ms);
}
}
}
else
{
if (ob1.GetType().IsArray)
{
var r1 = ob1.Rank;
object[] d1 = new object[r1];
long[] V1 = new long[r1];
for (int i = 0; i < r1; i++)
{
V1[i] = 0;
d1[i] = ob1.GetUpperBound(i) + 1;
}
ob2 = Activator.CreateInstance(ob1.GetType(), d1);
for (long i = 0; i <= ob2.Length; i++)
{
ob2.SetValue(DeepClone(ob1.GetValue(V1)), V1);
for (int j = 0; j <= V1.GetUpperBound(0); j++)
{
if (V1[j] < ob2.GetUpperBound(j))
{
V1[j]++;
break;
}
else
{
V1[j] = 0;
}
}
}
}
else
{
PropertyInfo[] P1 = ob1.GetType().GetProperties();
ob2 = Activator.CreateInstance(ob1.GetType());
foreach (PropertyInfo p1 in P1)
{
try
{
if (p1.PropertyType.GetInterface("System.Collections.ICollection", true) != null)
{
dynamic V2 = p1.GetValue(ob1) as IEnumerable;
MethodInfo gm1 = p1.PropertyType.GetMethods().Where(m => m.Name == "Add").Where(p => p.GetParameters().Count() == 1).First(f => V2[0].GetType().IsSubclassOf(f.GetParameters()[0].ParameterType) || f.GetParameters()[0].ParameterType == V2[0].GetType());
if (V2[0].GetType().IsSubclassOf(gm1.GetParameters()[0].ParameterType) || gm1.GetParameters()[0].ParameterType == V2[0].GetType())
{
for (int i = 0; i < V2.Count; i++)
{
dynamic V3 = DeepClone(V2[i]);
gm1.Invoke(p1.GetValue(ob2), new[] {V3});
}
}
}
}
catch (Exception ex)
{
}
}
foreach (PropertyInfo p1 in P1)
{
try
{
if (p1.PropertyType.IsSerializable && p1.CanWrite)
{
var v2 = p1.GetValue(ob1);
p1.SetValue(ob2, v2);
}
if (!p1.PropertyType.IsSerializable && p1.CanWrite)
{
dynamic V2 = p1.GetValue(ob1);
if (p1.PropertyType.GetMethod("Clone") != null)
{
dynamic v1 = V2.Clone();
p1.SetValue(ob2, v1);
}
}
}
catch (Exception ex)
{
}
}
}
}
return ob2;
}
你可能会说,这种方法不会复制某些种类的财产,但它确实主要性能的复制和克隆控制看起来像原来的控制!
试图克隆一个控制,除非你真的需要一个完全通用的控制clone方法矫枉过正。 大多数时候,你只需要复制特定的控制和你有一个轻松访问创建它的代码(见窗体设计器生成的代码,并设置代码你自己写)。 但尽管如此,我曾经用一招,以填补一个TabControl的新标签一次复制许多控件,选择十分之一的标签设计。 我也想用C#IDE的表单设计工具进行编辑和修改10模板。 所以,除了我的标签控件形式,以及使用VS IDE,我在我的项目在10“控制工厂的虚拟形式”。 我把每一个它的假面板控制。 每次我必须动态地创建一个新的标签,我只是实例化所需样式的一个新的虚拟窗口。 然后,我只是感动家长窗格我ControlTab(使用新的标签的Controls.Add被()方法)。 通过这种方式,则必须在标签创建后链接的事件处理程序(控件移动后)。 而事件处理程序的代码应该写在你的主窗口类,否则就会有“本”引用问题。
很明显,你将不得不存储控制引用的地方,才能够访问它们。 要做到这一点最简单的方法是只跟踪每个“虚拟模板表”实例化和设置您的控件的“调节剂”是“大众”。 您可以使用目的地标签页的Tag属性来存储参考。 但是,为了避免许多铸件,最好是要申报每个窗体类的数组,并存储在那里的引用。
文章来源: How to Clone a Windows Forms Controls even with non-Serializable properties?