C# 行.Add和行.AddRange与自定义DataGridViewRow之间的.Net DataGridView差异

C# 行.Add和行.AddRange与自定义DataGridViewRow之间的.Net DataGridView差异,c#,datagridview,datagridviewrow,C#,Datagridview,Datagridviewrow,DataGridViewControl的奇怪行为今天花了我不少时间 TL;DR:Rows.Add(MyOwnFancyRow)将MyOwnFancyRow的克隆添加到DGV,而Rows.AddRange(new MWERow[]{MyOwnFancyRow})添加MyOwnFancyRow本身。在写了所有这些之后,我明白了,但仍然不明白为什么 假设:DataGridView.Rows.Add()和DataGridView.Rows.AddRange()根据行模板处理新行的插入 实验:我使用unb

DataGridViewControl
的奇怪行为今天花了我不少时间

TL;DR
Rows.Add(MyOwnFancyRow)
MyOwnFancyRow
的克隆添加到DGV,而
Rows.AddRange(new MWERow[]{MyOwnFancyRow})
添加
MyOwnFancyRow
本身。在写了所有这些之后,我明白了,但仍然不明白为什么

假设:
DataGridView.Rows.Add()
DataGridView.Rows.AddRange()
根据
行模板处理新行的插入

实验:我使用unbound
DataGridView
s,设计用于显示复杂类,并为用户提供根据自己的喜好配置此类实例的方法。我可以从XML加载一些实例并显示它们,同时也可以给用户添加新实例的机会(想想
dgv.allowUserToAddress=true;

为了正确显示这些实例,我创建了自定义的
DataGridViewRow
s来处理用户和实例之间的所有交互。这样,我就可以将我的实例插入另一个完全不同形式的
DataGridView
,而无需复制所有处理代码。这一排为我做了这一切

请为我的最小(非)工作示例考虑这不是过于复杂的类:

class MWEObject : ICloneable {
    public string Value1 { get; internal set; }
    public double Value2 { get; internal set; }

    public delegate void OnMWEObjectChanged();
    public event OnMWEObjectChanged ObjectChanged;

    public MWEObject() {
        Value1 = "Default"; Value2 = 0;
    }

    public MWEObject(string in_Value1, double in_Value2) {
        Value1 = in_Value1; Value2 = in_Value2;
    }

    public void UpdateObject(MWEObject in_Object) {
        Value1 = in_Object.Value1;
        Value2 = in_Object.Value2;
        if (ObjectChanged != null)
            ObjectChanged.Invoke();
    }

    public override string ToString() {
        return "Value1: " + Value1 + " Value2: " + Value2.ToString("0.000");
    }

    public object Clone() {
        return new MWEObject(Value1, Value2);
    }
}
这个类本身无法轻松地进入
DataGridView
,因此这里有一个自定义的
DataGridView行
来处理显示和更新(我试图使它尽可能完整但尽可能简短,这样概念就变得清晰,我可以指出错误):

这一行背后的概念如下:

  • 静态方法允许我设置所需的任何
    DataGridView
  • DataGridView
    将指定特定的事件处理程序来处理添加行和编辑单元格=>这样可以保持表单的代码干净,并且可以在添加行后显示正确的值
  • 列被添加到
    DataGridView
  • CellTemplate
    设置为最基本的行=>其中使用默认值实例化对象(在“添加新行”行中显示值,默认值总是很好)
  • 请注意克隆方法(根据文档,该方法必须存在)。我们将在短时间内移动评论
行本身首先创建所需的单元格(
CreateCells(templateDGV);
),这就是我必须提供指向该行将要添加到的DataGridView的链接的原因=>我需要列名

要完成所有工作,我们需要一个名为dgv的表单,其中包含1个
DataGridView
和6个
按钮
s,因此它是:

public partial class frm_Test : Form {
    List<MWEObject> theObjects;
    List<MWEObject> theSecondObjects;
    public frm_Test() {
        InitializeComponent();
        theObjects = new List<MWEObject>()
        {
            new MWEObject("test 1", 1),
            new MWEObject("test 2", 2),
            new MWEObject("test 3", 3),
            new MWEObject("test 4", 4)
        };
        theSecondObjects = new List<MWEObject>();
    }

    private void btn_SetupDGV_Click(object sender, EventArgs e) {
        MWERow.SetupDGV(dgv);
        dgv.RowsAdded += Dgv_RowsAdded;
    }

    private void Dgv_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e) {
        DataGridView dgv = sender as DataGridView;
        if (dgv != null)
        {
            int StartIndex = e.RowIndex;
            if ((dgv.AllowUserToAddRows && StartIndex != 0) && StartIndex == dgv.Rows.Count - 1)
                StartIndex--;
            foreach (int i in Enumerable.Range(StartIndex, e.RowCount))
                theSecondObjects.Add(((MWERow)dgv.Rows[i]).theObject);
        }
    }

    private void btn_ShowLists_Click(object sender, EventArgs e) {
        MessageBox.Show("Rows: " + Environment.NewLine + string.Join(Environment.NewLine, dgv.Rows.Cast<MWERow>().Select(x => x.theObject.ToString() + (x.IsNewRow ? " isNew" : ""))));
        MessageBox.Show("List: " + Environment.NewLine + string.Join(Environment.NewLine, theObjects.Select(x => x.ToString())));
        MessageBox.Show("Second List: " + Environment.NewLine + string.Join(Environment.NewLine, theSecondObjects.Select(x => x.ToString())));
    }

    private void btn_AddSingleItem_Click(object sender, EventArgs e) {
        dgv.Rows.Add(new MWERow(new MWEObject("1 Test", 1), dgv));
        dgv.Rows.Add(new MWERow(new MWEObject("2 Test", 2), dgv));
        dgv.Rows.Add(new MWERow(new MWEObject("3 Test", 3), dgv));
        dgv.Rows.Add(new MWERow(new MWEObject("4 Test", 4), dgv));
        dgv.Rows.Add(new MWERow(new MWEObject("5 Test", 5), dgv));
    }

    private void btn_AddBulkItems_Click(object sender, EventArgs e) {
        dgv.Rows.AddRange(new MWERow[]
            {
                new MWERow(new MWEObject("1 Test", 1), dgv),
                new MWERow(new MWEObject("2 Test", 2), dgv),
                new MWERow(new MWEObject("3 Test", 3), dgv),
                new MWERow(new MWEObject("4 Test", 4), dgv),
                new MWERow(new MWEObject("5 Test", 5), dgv)
            });
    }

    private void btn_AddSingleList_Click(object sender, EventArgs e) {
        foreach (MWEObject o in theObjects)
            dgv.Rows.Add(new MWERow(o, dgv));
    }

    private void btn_AddBulkList_Click(object sender, EventArgs e) {
        dgv.Rows.AddRange(theObjects.Select(x => new MWERow(x, dgv)).ToArray());
    }
}
公共部分类frm_测试:表格{
列出目标;
列出这些共有对象;
公共frm_测试(){
初始化组件();
对象=新列表()
{
新MWEObject(“测试1”,1),
新MWEObject(“测试2”,2),
新MWEObject(“测试3”,3),
新MWEObject(“测试4”,4)
};
theSecondObjects=新列表();
}
私有无效btn_设置DGV_单击(对象发送者,事件参数e){
MWERow.设置dgv(dgv);
dgv.RowsAdded+=dgv_RowsAdded;
}
私有void Dgv_RowsAdded(对象发送方,DataGridViewRowsAddedEventArgs e){
DataGridView dgv=作为DataGridView的发送方;
如果(dgv!=null)
{
int StartIndex=e.RowIndex;
if((dgv.allowUserToAddress&&StartIndex!=0)&&StartIndex==dgv.Rows.Count-1)
StartIndex--;
foreach(可枚举范围中的int i(StartIndex,e.RowCount))
这些condobjects.Add(((MWERow)dgv.Rows[i])。对象);
}
}
私有无效btn\u显示列表\u单击(对象发送者,事件参数e){
MessageBox.Show(“行:”+Environment.NewLine+string.Join(Environment.NewLine,dgv.Rows.Cast()。选择(x=>x.theObject.ToString()+(x.IsNewRow?“isNew”)));
Show(“列表:+Environment.NewLine+string.Join(Environment.NewLine,theObjects.Select(x=>x.ToString())));
Show(“第二个列表:”+Environment.NewLine+string.Join(Environment.NewLine,theSecondObjects.Select(x=>x.ToString()));
}
私有void btn\u AddSingleItem\u单击(对象发送者,事件参数e){
dgv.Rows.Add(新MWERow(新MWEObject(“1测试”,1),dgv));
dgv.Rows.Add(新MWERow(新MWEObject(“2测试”,2),dgv));
dgv.Rows.Add(新MWERow(新MWEObject(“3测试”,3),dgv));
dgv.Rows.Add(新MWERow(新MWEObject(“4测试”,4),dgv));
dgv.Rows.Add(新MWERow(新MWEObject(“5测试”,5),dgv));
}
私有void btn\u AddBulkItems\u单击(对象发送者,事件参数e){
dgv.Rows.AddRange(新MWERow[]
{
新MWERow(新MWEObject(“1测试”,1),dgv),
新MWERow(新MWEObject(“2测试”,2),dgv),
新MWERow(新MWEObject(“3测试”,3),dgv),
新MWERow(新MWEObject(“4测试”,4),dgv),
新MWERow(新MWEObject(“5测试”,5),dgv)
});
}
私有void btn\u AddSingleList\u单击(对象发送者,事件参数e){
foreach(对象中的MWEObject o)
添加(新的MWERow(o,dgv));
}
私有无效btn\u添加批量列表\u单击(对象发送者,事件参数e){
dgv.Rows.AddRange(theObjects.Select(x=>newmwerow(x,dgv)).ToArray();
}
}
按钮功能的简短说明:

  • 一个按钮设置DataGridView
  • 一个按钮显示:所有行中的“对象”、“对象”列表的内容(固定)和“对象”列表的内容(随着每次添加而增加)
  • 一个按钮添加新行insta
    public partial class frm_Test : Form {
        List<MWEObject> theObjects;
        List<MWEObject> theSecondObjects;
        public frm_Test() {
            InitializeComponent();
            theObjects = new List<MWEObject>()
            {
                new MWEObject("test 1", 1),
                new MWEObject("test 2", 2),
                new MWEObject("test 3", 3),
                new MWEObject("test 4", 4)
            };
            theSecondObjects = new List<MWEObject>();
        }
    
        private void btn_SetupDGV_Click(object sender, EventArgs e) {
            MWERow.SetupDGV(dgv);
            dgv.RowsAdded += Dgv_RowsAdded;
        }
    
        private void Dgv_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e) {
            DataGridView dgv = sender as DataGridView;
            if (dgv != null)
            {
                int StartIndex = e.RowIndex;
                if ((dgv.AllowUserToAddRows && StartIndex != 0) && StartIndex == dgv.Rows.Count - 1)
                    StartIndex--;
                foreach (int i in Enumerable.Range(StartIndex, e.RowCount))
                    theSecondObjects.Add(((MWERow)dgv.Rows[i]).theObject);
            }
        }
    
        private void btn_ShowLists_Click(object sender, EventArgs e) {
            MessageBox.Show("Rows: " + Environment.NewLine + string.Join(Environment.NewLine, dgv.Rows.Cast<MWERow>().Select(x => x.theObject.ToString() + (x.IsNewRow ? " isNew" : ""))));
            MessageBox.Show("List: " + Environment.NewLine + string.Join(Environment.NewLine, theObjects.Select(x => x.ToString())));
            MessageBox.Show("Second List: " + Environment.NewLine + string.Join(Environment.NewLine, theSecondObjects.Select(x => x.ToString())));
        }
    
        private void btn_AddSingleItem_Click(object sender, EventArgs e) {
            dgv.Rows.Add(new MWERow(new MWEObject("1 Test", 1), dgv));
            dgv.Rows.Add(new MWERow(new MWEObject("2 Test", 2), dgv));
            dgv.Rows.Add(new MWERow(new MWEObject("3 Test", 3), dgv));
            dgv.Rows.Add(new MWERow(new MWEObject("4 Test", 4), dgv));
            dgv.Rows.Add(new MWERow(new MWEObject("5 Test", 5), dgv));
        }
    
        private void btn_AddBulkItems_Click(object sender, EventArgs e) {
            dgv.Rows.AddRange(new MWERow[]
                {
                    new MWERow(new MWEObject("1 Test", 1), dgv),
                    new MWERow(new MWEObject("2 Test", 2), dgv),
                    new MWERow(new MWEObject("3 Test", 3), dgv),
                    new MWERow(new MWEObject("4 Test", 4), dgv),
                    new MWERow(new MWEObject("5 Test", 5), dgv)
                });
        }
    
        private void btn_AddSingleList_Click(object sender, EventArgs e) {
            foreach (MWEObject o in theObjects)
                dgv.Rows.Add(new MWERow(o, dgv));
        }
    
        private void btn_AddBulkList_Click(object sender, EventArgs e) {
            dgv.Rows.AddRange(theObjects.Select(x => new MWERow(x, dgv)).ToArray());
        }
    }