Gleicher Zeilen-Inhalt nach Sortieren beim DataGridView
Frank Dzaebel, erstellt am: 11.2.2006, zuletzt geändert:  1.6.2008
Kategorie: DataGridView, .NET-Version: 2.0

Das DataGridView setzt die Binding-Position nach einem Klick auf den ColumnHeader unter .NET 1.1/2.0 nicht um, wenn das DataGridView etwa an DataTable gebunden ist (gebundenes Szenario). In ungebundenen Szenarien funktioniert bereits ein einfaches Setzen von FirstDisplayedScrollingRowIndex auf den RowIndex der gewünschten Zelle. Das bedeutet, dass der Inhalt der aktuellen Position nach dem (Sortier-) Klick anders ist. Um den gleichen Inhalt zu ermöglichen ist folgender Beispiel-Code eine Variante:

DataRow curRow;
CurrencyManager cm;

private void Form1_Load(object sender,EventArgs e)
{
  this.adresseTableAdapter.Fill(this.frankDataSet.Adresse); //Beispiel
  cm = (CurrencyManager)BindingContext[dataGridView1.DataSource,dataGridView1.DataMember];
  curRow = ((DataRowView)cm.List[cm.Position]).Row;
  cm.PositionChanged += new EventHandler(cm_PositionChanged);
  dataGridView1.MouseUp += new MouseEventHandler(dataGridView1_MouseUp);
}

void cm_PositionChanged(object sender,EventArgs e)
{ curRow = ((DataRowView)cm.List[cm.Position]).Row;      
}

void dataGridView1_MouseUp(object sender,MouseEventArgs e)
{
  if (dataGridView1.HitTest(e.X,e.Y).Type != DataGridViewHitTestType.ColumnHeader) return;
  DataRow dr; if (curRow == null) return;
  object[] curArr = curRow.ItemArray;
  for (int i = 0; i < cm.List.Count; i++)
  {
    dr = ((DataRowView)cm.List[i]).Row;  
    bool equals = true;
    for (int j = 0; j < curArr.Length; j++)
      if (curArr[j].ToString() != dr[j].ToString()) 
         {equals = false; break;}
    if (equals) { cm.Position = i; dataGridView1.Rows[i].Selected = true; break; }
  }
}

Bei Primärschlüssel-Existenz performanter und sauberer:

Der oben angegebene Weg funktioniert auch, wenn der DataTable keinen primären Schlüssel hat, weil er brute force alle Werte einer Spalte gegeneinander vergleicht, um die Identität der Zeile herauszufinden. Wenn es aber Zeilen gäbe, die vom Wert her komplett gleich wären, könnten ggf. Positionierungsfehler auftreten und das Verfahren kann bei grossen Datenmengen zu langsam sein.
Deswegen - wenn es einen Primärschlüssel in der Tabelle "Adresse" gibt, kann folgendes performanter und sauberer sein:

DataRow curRow;
CurrencyManager cm;
string pkSpaltenName;

private void Form1_Load(object sender,EventArgs e)
{
  this.adresseTableAdapter.Fill(this.frankDataSet.Adresse); //Beispiel
  pkSpaltenName = frankDataSet.Adresse.PrimaryKey[0].ColumnName;
  cm = (CurrencyManager)BindingContext[dataGridView1.DataSource,dataGridView1.DataMember];
  curRow = ((DataRowView)cm.List[cm.Position]).Row;
  cm.PositionChanged += new EventHandler(cm_PositionChanged);
  dataGridView1.MouseUp += new MouseEventHandler(dataGridView1_MouseUp);
}

void cm_PositionChanged(object sender,EventArgs e)
{ curRow = ((DataRowView)cm.List[cm.Position]).Row;      
}

void dataGridView1_MouseUp(object sender, MouseEventArgs e)
{
  if (dataGridView1.HitTest(e.X, e.Y).Type != DataGridViewHitTestType.ColumnHeader) return;
  if (curRow == null) return;
  int positionID = adresseBindingSource.Find(pkSpaltenName, curRow[pkSpaltenName]);
  adresseBindingSource.Position = positionID;
  if (!dataGridView1.Rows[positionID].Displayed) 
dataGridView1.FirstDisplayedScrollingRowIndex = positionID; }

Im ungebundenen Szenario ggf. einfach:
DataGridView dgv = new DataGridView(); private void Form1_Load(object sender, EventArgs e) { Controls.Add(dgv); dgv.Dock = DockStyle.Fill; dgv.Columns.Add("Spalte1", "Spalte1"); dgv.Columns.Add("Spalte2", "Spalte2"); for (int i = 0; i < 200; i++) dgv.Rows.Add("Wert" + i.ToString() + " 0", "Wert" + i.ToString() + " 1"); dgv.Sorted += new EventHandler(dgv_Sorted); } void dgv_Sorted(object sender, EventArgs e) { dgv.FirstDisplayedScrollingRowIndex = dgv.CurrentRow.Index; }