How to create a new DataGridViewRow and specify values using column names, not indices?

0

I have a DataGridView. I try to add rows to it by cloning the add-new-row row. When I add a row, I set some of the values of its cells. I try to access the DataGridViewCell instances by the Name of their column.

Before the UI is shown, I do something similar to this in a subclass of DataGridView:

MyDataGridViewRow r = Rows[NewRowIndex].Clone() as MyDataGridViewRow;
Rows.Add(r);
r.Cells[Columns["MyColumnName"].Index].Value = "Example text";

This does not work because I get (on the last line) this exception:

System.ArgumentOutOfRangeException: 'Specified argument was out of the range of valid values. Parameter name: rowIndex'

I guess that the cells are added to the corresponding column, but not to the row.

If I directly use DataGridViewRow.Cells[] operator with the column name Tag:

System.ArgumentException: 'Column named Tag cannot be found. Parameter name: columnName'

When this happens, I see in the debugger that the Cells collection contains all the columns, and each Cell has

  • the ColumnIndex and RowIndex equal to -1 if I try the [] operator before Rows.Add, and
  • only the RowIndex is equal to -1, if I call Rows.Add before the [] operator (in this case the ColumnIndex have values starting from 0).

This happens before any form is shown.

So I tried to make a small project that has the same bug. This is in the Form1 class:

DataGridView dgv;

public Form1()
{
    InitializeComponent();

    dgv = new DataGridView();

    // from DataGridView subclass ctor
    dgv.RowTemplate = new MyRow();

    dgv.Left = 10;
    dgv.Top = 10;
    Controls.Add(dgv);

    // from Initialize method
    dgv.Columns.Clear();
    DataGridViewTextBoxColumn c = new DataGridViewTextBoxColumn()
    {
        HeaderText = "My Header",
        Name = "MyColumn"
    };
    dgv.Columns.Add(c);

    // here the columns and row templates should be ready
    MyRow r = dgv.Rows[dgv.NewRowIndex].Clone() as MyRow;
    r.Cells["MyColumn"].Value = "My Value";
    dgv.Rows.Add(r);
}

internal class MyRow : DataGridViewRow
{
    internal MyRow() : base()
    {
    }
}

Now I get another exception on the line dgv.Columns.Add(c):

System.MissingMethodException: 'No parameterless constructor defined for this object.'

Detalis of the exception:

System.MissingMethodException
  HResult=0x80131513
  Message=No parameterless constructor defined for this object.
  Source=mscorlib
  StackTrace:
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at System.Windows.Forms.DataGridViewRow.Clone()
   at System.Windows.Forms.DataGridView.get_RowTemplateClone()
   at System.Windows.Forms.DataGridViewRowCollection.AddInternal(Boolean newRow, Object[] values)
   at System.Windows.Forms.DataGridView.AddNewRow(Boolean createdByEditing)
   at System.Windows.Forms.DataGridView.OnColumnCollectionChanged_PostNotification(DataGridViewColumn dataGridViewColumn)
   at System.Windows.Forms.DataGridViewColumnCollection.OnCollectionChanged_PostNotification(CollectionChangeEventArgs ccea, Boolean changeIsInsertion, Point newCurrentCell)
   at System.Windows.Forms.DataGridViewColumnCollection.Add(DataGridViewColumn dataGridViewColumn)
   at project_namespace.Form1..ctor() in {project folder path here}\Form1.cs:line 37
   at project_namespace.Program.Main() in {project folder path here}\Program.cs:line 19

Related question here.

I am using .NET Framework 4.6.1.

Update 1:

If I change the dgv.Columns.Add(c); line to

dgv.HandleCreated += (s, e) => { dgv.Columns.Add(c); };

I get the following exception on the line that starts with MyRow r:

System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index'

Exception details:

System.ArgumentOutOfRangeException
  HResult=0x80131502
  Message=Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
  Source=mscorlib
  StackTrace:
   at System.Collections.ArrayList.get_Item(Int32 index)
   at System.Windows.Forms.DataGridViewRowCollection.SharedRow(Int32 rowIndex)
   at System.Windows.Forms.DataGridViewRowCollection.get_Item(Int32 index)
   at cs_test_6.Form1..ctor() in .....\Form1.cs:line 45
   at cs_test_6.Program.Main() in .....\Program.cs:line 19

and if I remove the last three lines in the constructor, I get the same first error on the line with dgv.Columns.Add(c) now inside the one-line anonymous function.

Update 2: What I have learned:

  • the row template class must have a public constructor;
  • rows should be added after columns;
  • I can use the row template class with some properties in it to set columns by name, but it is still unclear to me;

If I load the order of the columns from a file, and they can be reordered, how can I use the row template to set the values of the cells by name? This code works just like Rows.Add(val1, val2) (but probably it is less efficient):

MyDataGridViewRow r = new MyDataGridViewRow()
{

};
r.CreateCells(this);
r.Cells[0] =
    new DataGridViewTextBoxCell()
    {
        Value = val1
    };
r.Cells[1] =
    new DataGridViewTextBoxCell()
    {
        Value = val2
    };
Rows.Add(r);

If I try to use r.Cells["MyColumn"] I get the error shown at the beginning of the question.

My question is: How and when can I reference the cells by column name inside a subclass of DataGridViewRow? I tried to do this in the constructor, but it does not work, not even with integer indices. I need to do this because I want to have reorderable columns.

c#
winforms
datagridview
datagridviewcolumn
asked on Stack Overflow Nov 29, 2018 by silviubogan • edited Dec 5, 2018 by silviubogan

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0