【问题标题】:Custom DataGridView Column duplicates when using it in Designer在设计器中使用时自定义 DataGridView 列重复
【发布时间】:2016-04-08 14:10:22
【问题描述】:

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

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

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

当我在不更改任何内容的情况下启动它时,它会显示两列。第一个工作正常,第二个总是显示丢失的 x 图像(其中没有数据,所以它们都显示 x)。

在表单的设计器中有一个自动添加的图像列。

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 adds columns everytime I build 中所说的那样。没有任何变化。

清除表单上列出的列会删除工作列并留下一个列 dataGridViewImageColumn1,它只是一个名称错误的完整空列。

【问题讨论】:

  • 你在datagridview的设计器中添加了多少列?
  • 无。我只是将组件放在表单上并将 Dock 样式设置为 Fill。 VS 自己添加了列。
  • 控件按预期运行。您在构造函数中添加了一列,然后设计器序列化该列。然后运行应用程序,它在构造函数中添加一列。所以你有2列。并且以这种方式继续下去,并且不限于 2 列。即使您不需要构建和运行,打开包含网格的表单并在表单中进行一些小的更改并保存就足够了。一个新的专栏诞生了!

标签: c# .net winforms datagridview windows-forms-designer


【解决方案1】:

控件按预期运行。

原因

您在构造函数中添加了一个列,然后设计器序列化了该列。然后运行应用程序,它在构造函数中添加一列。所以你有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. 关于第三种解决方案。至少目前您可以使用以前的两种简单解决方案,但我不建议您遵循它。因为DataGridView 有自己的设计器名称DataGridViewDesigner 是内部的,你不能从它继承,如果你只是因为这个要求创建一个设计器,你会错过原始设计器的一些功能。也许您可以解决这个问题,甚至创建一个ToolBoxItem 而不是设计器,但您不需要它。此处的选项可让答案更完整,对此类案例的未来读者更有用。

【讨论】:

  • 我尝试了第一个。 if (!DesignMode) 围绕列创建和添加,但无论如何它都在设计器中。还是我有什么问题?第二种方法是我以前做的。当那里没有正确的列时,我在我的属性的 setter 中创建了列,但它不是很有用,因为我找不到哪一列应该存在,哪一列不存在。在生产中,我为不同的图像列使用了两个属性。我去阅读一些有关自定义设计器解决方案的信息。
  • 创建新列时,我没有找到确切发生的时间,但在表单上工作了一段时间后,我的表单中有大约 40 列,而不是应该存在的两列 @.@
  • 原因正是我所说的。即使您不需要构建和运行,打开包含网格的表单并在表单中进行一些小的更改并保存就足够了。一个新的专栏诞生了!
  • 关于解决方案,这取决于您的要求,例如,您可能使用 DesignMode 属性检查了设计模式,这在构造函数中对您不起作用。相反,您应该使用if (System.ComponentModel.LicenseManager.UsageMode != LicenseUsageMode.Designtime)
  • 关于第二种解决方案,例如添加一个新的MyCustomImageColumn类型就足够了,只检查MyCustomImageColumn类型的列是否存在,如果存在,不要添加另一个,因为一个就够了。
【解决方案2】:

人们多年来一直在与这个问题作斗争,但正如 Reza 所说,这是预期的行为 - 因此它从未被“修复”。有很长的MSDN forum thread 讨论这个问题。在众多失败的解决方案中,Reza提供的成功建议大多在一定程度上有所暗示。

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

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

如果您想进行这种自定义,您应该使用 usercontrol 代替或使用配置您的共享功能 运行时的 gridview 属性。

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

【讨论】:

  • 解决方案取决于需求。在当前问题中,我更喜欢使用继承并将该属性设置为仅运行时属性。 用户控件解决方案也适用于许多情况,但它有一些问题,特别是这个问题:这样您就失去了在设计器中使用托管控件的能力。
  • 对于我们不需要仅运行时属性的情况,我建议创建一个自定义 Designer(如果您不错过最初的设计者) 并覆盖InitializeNewComponent 或作为另一个适用选项创建自定义ToolBox 项目并覆盖CreateComponentsCore
  • 关于设计器中托管控件的要点。第二条评论可能同样有用,但我承认我必须研究它以进一步理解。
猜你喜欢
  • 2014-09-07
  • 1970-01-01
  • 2016-09-26
  • 2016-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多