Access DataGridView rows the order they were added
Asked Answered
H

1

6

I have a DataGridView which colors its rows when its States property is set.
States is a String which represents a semicolon-separated numbers list.

If I receive "0;1;2" the three first rows will be colored in purle, green and red respectively.
The problem comes when I sort the datagrid clicking on a column header : colors are applied the same way.

For example :

Names|Labels  
Name1|Label1  
Name2|Label2  
Name3|Label3 

I receive "0;1;2" which means "Purple;Green;Red" :

Names|Labels  
Name1|Label1 => Purple  
Name2|Label2 => Green  
Name3|Label3 => Red 

I sort (descending):

Names|Labels  
Name3|Label3 => Red  
Name2|Label2 => Green  
Name1|Label1 => Purple  

I receive "3;4;5" which means "Yellow;Orange;Pink" :

Names|Labels  
Name3|Label3 => Yellow  
Name2|Label2 => Orange  
Name1|Label1 => Pink 

But this isn't what I was waiting for, I wanted that :

Names|Labels  
Name3|Label3 => Pink  
Name2|Label2 => Orange  
Name1|Label1 => Yellow  

Here is my code :

protected String m_States;

public virtual String States
{
  get { return m_States; }

  set {
    m_States = value;
    if (m_bRunning)
    {
      UpdateColors();
    }
  }
}

private void UpdateColors()
{
  String[] sStates = new String[] { };
  if (m_States != null)
  {
    sStates = m_States.Split(m_sSeparators);

    int nState = 0;
    int nRowNumber = 0;
    foreach (System.Windows.Forms.DataGridViewRow row in Rows)
    {
      nState = int.Parse(sStates[nRowNumber]);

      if (nState < 0 || nState > m_Couleurs_Fond_Etats.Length)
      {
        nState = m_Couleurs_Fond_Etats.Length - 1;
      }
      row.DefaultCellStyle.BackColor = m_Couleurs_Fond_Etats[nState];
      row.DefaultCellStyle.ForeColor = m_Couleurs_Texte_Etats[nState];
      row.DefaultCellStyle.SelectionBackColor = m_Couleurs_Sel_Fond_Etats[nState];
      row.DefaultCellStyle.SelectionForeColor = m_Couleurs_Sel_Texte_Etats[nState];

      nState = 0;
      ++nRowNumber;
    }
  }
}

Isn't there a way to have access to rows the order they were added in the DataGridView?

PS : I first used row.Index instead of nRowNumber, so I believed the problem came from that, but apparently, the Rows collection is reorganized or the foreach parses it according to the rowIndexes.

===== Here is the solution I used thanks to LarsTech's answer =====

After adding my rows, I tagged them this way :

foreach(System.Windows.Forms.DataGridViewRow row in Rows)
{
  row.Tag = row.Index;
}

Then I could use this tag as row number :

private void UpdateColors()
{
  String[] sStates = new String[] { };
  if (m_States != null)
  {
    sStates = m_States.Split(m_sSeparators);

    int nState = 0;
    int nRowNumber = 0;
    foreach (System.Windows.Forms.DataGridViewRow row in Rows)
    {
      nRowNumber = Convert.ToInt32(row.Tag);
      if (nRowNumber >= 0 && nRowNumber < sEtats.Length)
      {
        nState = int.Parse(sStates[nRowNumber]);

        if (nState < 0 || nState > m_Couleurs_Fond_Etats.Length)
        {
          nState = m_Couleurs_Fond_Etats.Length - 1;
        }
        row.DefaultCellStyle.BackColor = m_Couleurs_Fond_Etats[nState];
        row.DefaultCellStyle.ForeColor = m_Couleurs_Texte_Etats[nState];
        row.DefaultCellStyle.SelectionBackColor = m_Couleurs_Sel_Fond_Etats[nState];
        row.DefaultCellStyle.SelectionForeColor = m_Couleurs_Sel_Texte_Etats[nState];

        nState = 0;
      }
    }
  }
}
Hone answered 22/6, 2012 at 9:10 Comment(0)
L
4

You can try using the Tag property of the row to place your row index number. This is how I had my DataGridView initialized:

dataGridView1.Rows.Add(3);
for (int i = 0; i < 3; i++) {
  dataGridView1.Rows[i].Tag = i;
  dataGridView1.Rows[i].Cells[0].Value = "Name " + i.ToString();
  dataGridView1.Rows[i].Cells[1].Value = "Label " + i.ToString();
}

Here is my version of your UpdateColors routine:

private void UpdateColors() {
  String[] sStates = new String[] { };
  if (m_States != null) {
    sStates = m_States.Split(';');
    for (int i = 0; i < sStates.Length;i++) {
      int nState = Convert.ToInt32(sStates[i]);
      foreach (DataGridViewRow row in dataGridView1.Rows) {
        int rowIndex = Convert.ToInt32(row.Tag);
        if (rowIndex == i) {
          row.DefaultCellStyle.BackColor = m_Couleurs_Fond_Etats[nState];
        }
      }
    }
  }
}

I am first looping through your splitted state string to get the row index and to convert the actual value to the color index. Then I loop through the rows to find the matching index property that I placed in the Tag property.

Here is a cleaned up version using just the one loop:

private void UpdateColors() {
  String[] sStates = new String[] { };
  if (m_States != null) {
    sStates = m_States.Split(';');
    foreach (DataGridViewRow row in dataGridView1.Rows) {
      int rowIndex = Convert.ToInt32(row.Tag);
      int colorIndex = Convert.ToInt32(sStates[rowIndex]);
      row.DefaultCellStyle.BackColor = m_Couleurs_Fond_Etats[colorIndex];
    }
  }
}

Needs error checking obviously.

Lubet answered 22/6, 2012 at 13:30 Comment(4)
Thank you very much, that works. I'll remember the Tag trick next time! ^^Hone
Since the tag property is an object, I don't think that there is any reason to store the tag as a string. you may want to try using "dataGridView1.Rows[i].Tag = i" and then "int rowIndex = i" in place of the Tostring() and Convert.ToInt versions.Proclamation
Personaly, I prefer looping over rows and access my state thanks to the current row tag (if possible). That prevents you from looping over all the rows for each state. But perhaps I missed something, I'm new to C#.Hone
@Hone Yes, my original answer does have that unnecessary loop.Lubet

© 2022 - 2024 — McMap. All rights reserved.