自定义DataGridView列在Designer中使用时重复

我制作了一个自定义的DataGridView组件,里面有一个标准的DataGridViewImageColumn。 当我不需要在特定表中输入时,新属性会更改列的可见性。 我在构造函数中添加列并在CellFormatting事件上更新它。 这是一个像预期一样工作的部分。

当我将控件放到新表单上时,它会显示新列。 运行程序会在网格中生成两个图像列。

设计师截图

一个新表单刚刚添加了组件并设置了Dock.Fill

应用程序运行截图

当我开始它而不改变任何东西时,它显示了两列。 第一个是它应该工作,第二个总是显示丢失的x图像(没有数据,因此它们显示x)。

在表单的设计者中,会自动添加一个ne image列。

private CustomDataGridView customDataGridView1; private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn1; 

当我继续在表单中进行编辑时,有时会发生VS创建更多相同列的副本。 要解决这个问题,我必须时不时地清除DGV.Columns列表。

如何防止VS复制我的字段?

以下代码是重现问题的最小部分。

 public class CustomDataGridView : DataGridView { private DataGridViewImageColumn EditStatusIcons; private bool hasIcons = true; public bool HasIcons { get { return this.hasIcons; } set { if (this.Columns["EditIcons"] == null) return; this.Columns["EditIcons"].Visible = value; this.hasIcons = value; } } public CustomDataGridView() { this.EditStatusIcons = new System.Windows.Forms.DataGridViewImageColumn(); this.EditStatusIcons.HeaderText = ""; this.EditStatusIcons.Name = "EditStatusIcons"; this.Columns.Add(this.EditStatusIcons); } } 

编辑:我也尝试使用this.AutoGenerateColumns = false; 就像在Custom DataGridView中每次构建时都会添加列 。 没有什么变化。

清除表单上的列列表将删除工作列并保留列dataGridViewImageColumn1,该列只是一个名称错误的完整空列。

控制按预期运行。

原因

您在构造函数中添加了一列,然后设计器序列化该列。 然后运行应用程序,它在构造函数中添加一列。 所以你有2列。 这种方式继续这样,并不限于2列。 即使您不需要构建和运行,也足以打开包含网格的表单并对表单进行少量更改并保存。 新专栏诞生了!

解决方案

根据您的要求,要解决问题,您可以考虑以下选项:

  1. 检查控件是否处于设计模式,然后不添加列并仅在运行时添加它。

  2. 您可以检查控件是否包含现有的此类图像列,然后不添加另一个。 您可以通过某些属性的类型使列唯一。

  3. 您可以为控件创建自定义设计器,并在控件初始化中添加添加列的代码。 这样,代码仅在第一次将控件添加到表单时运行。


关于解决方案的一些注

在解决方案之间进行选择完全基于您的要求,并且所有选项都可用。 但请考虑以下关于解决方案的说

  1. 关于第一个解决方案,不要使用DesignMode属性,因为它在构造函数中不起作用。 而是以这种方式执行检查:

     if (System.ComponentModel.LicenseManager.UsageMode != LicenseUsageMode.Designtime) { //The control is not in design mode, add the column here. } 
  2. 关于第二个解决方案,例如,它足以添加一个新的MyCustomImageColumn类型,并且只检查MyCustomImageColumn类型的列是否存在,如果它存在,则不要添加另一个,因为其中一个就足够了。

  3. 关于第3个解决方案。 至少目前你可以使用以前的两个简单解决方案我不建议遵循它。 因为DataGridView有自己的Designer名称DataGridViewDesigner ,它是内部的,你不能inheritance它,如果你只是因为这个要求而创建一个设计器,你将会错过原始设计师的一些function。 您可以解决此问题,甚至可以创建ToolBoxItem而不是设计器,但您不需要它。 这个选项可以使答案对于此类案例的未来读者更加完整和有用。

人们多年来一直反对这个问题,但正如Reza所说,这是预期的行为 – 因此它从未被“固定”过。 有一个冗长的MSDN论坛post讨论这个问题。 在众多失败的解决方案提案中, Reza提供的大多数成功建议都在某种程度上被提及。

在上面提到的线程中,用户CoolDadTx首先提出的第四个建议是将DataGridView包装在UserControl ,然后以这种方式添加列。 进一步推理:

使用inheritance设置属性是一种不好的做法,inheritance应该用于添加function或覆盖当前行为的行为。

如果要进行此类自定义,则应使用usercontrol或使用在运行时配置gridviewproperty的共享函数。

– Marco Guignard,2014年7月17日

优点是UserControl只会向您明确公开的设计器添加属性。 因此,您添加的列不会重复。 虽然它不像DesignTime if语句那样简洁,但用法可能如下所示:

 public class CustomControl : UserControl { private DataGridViewImageColumn EditStatusIcons; public DataGridView DGV; private bool hasIcons = true; public bool HasIcons { get { return this.hasIcons; } set { if (this.DGV.Columns["EditStatusIcons"] == null) return; this.dataGridView1.Columns["EditStatusIcons"].Visible = value; this.hasIcons = value; } } public CustomDataGridView() { this.EditStatusIcons = new System.Windows.Forms.DataGridViewImageColumn(); this.EditStatusIcons.HeaderText = ""; this.EditStatusIcons.Name = "EditStatusIcons"; this.DGV= new DataGridView(); this.DGV.Dock = DockStyle.Fill; this.DGV.Columns.Add(this.EditStatusIcons); this.Controls.Add(this.DGV); } }