Sortieren einer DataTable nach mehreren Kriterien

    • C#

    Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

      Sortieren einer DataTable nach mehreren Kriterien

      Hi Leute,
      die Aufgabe bestand darin, bei mehreren sehr ähnlichen Forms mit DGVs die Daten nach mehreren Kriterien zu sortieren.
      Es genügen hier einfache DataTables mit String-Spalten, da die Daten lediglich anzuzeigen waren.
      Die Formatierung der Daten wird gleich bei der Befüllung vorgenommen.
      Ich war auf der Suche nach einem einfachen Code, der für diese Belange ausreichte.
      Mit Anleihen bei Microsoft:
      msdn.microsoft.com/de-de/libra…ang=csharp#code-snippet-2
      docs.microsoft.com/de-de/dotne…orms-datagridview-control
      kam dann dieser Code heraus (Form mit einem DataGridView):
      Spoiler anzeigen

      C#-Quellcode

      1. using System;
      2. using System.Data;
      3. using System.Windows.Forms;
      4. namespace WindowsFormsApplication1
      5. {
      6. public partial class Form1 : Form
      7. {
      8. private DataTable Table;
      9. public Form1()
      10. {
      11. this.InitializeComponent();
      12. // damit C&P funktioniert
      13. this.dataGridView1.ColumnHeaderMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dataGridView1_ColumnHeaderMouseClick);
      14. this.Table = new DataTable("Sort");
      15. // Add four columns
      16. this.Table.Columns.Add("Col 1");
      17. this.Table.Columns.Add("Col 2");
      18. this.Table.Columns.Add("Col 3");
      19. this.Table.Columns.Add("Col 4");
      20. // Add data
      21. this.Table.Rows.Add("²²", "mm", "##", "++");
      22. this.Table.Rows.Add("²²", "mm", "##", "--");
      23. this.Table.Rows.Add("²²", "mm", "@@", "++");
      24. this.Table.Rows.Add("²²", "mm", "@@", "--");
      25. this.Table.Rows.Add("²²", "xx", "##", "++");
      26. this.Table.Rows.Add("²²", "xx", "##", "--");
      27. this.Table.Rows.Add("²²", "xx", "@@", "++");
      28. this.Table.Rows.Add("²²", "xx", "@@", "--");
      29. this.Table.Rows.Add("oo", "mm", "##", "++");
      30. this.Table.Rows.Add("oo", "mm", "##", "--");
      31. this.Table.Rows.Add("oo", "mm", "@@", "++");
      32. this.Table.Rows.Add("oo", "mm", "@@", "--");
      33. this.Table.Rows.Add("oo", "xx", "##", "++");
      34. this.Table.Rows.Add("oo", "xx", "##", "--");
      35. this.Table.Rows.Add("oo", "xx", "@@", "++");
      36. this.Table.Rows.Add("oo", "xx", "@@", "--");
      37. this.Table.AcceptChanges();
      38. this.dataGridView1.DataSource = this.Table;
      39. // Spaltennamen explizit überschreiben
      40. this.dataGridView1.Columns[0].HeaderText = "Spalte 1";
      41. this.dataGridView1.Columns[1].HeaderText = "Spalte 2";
      42. this.dataGridView1.Columns[2].HeaderText = "Spalte 3";
      43. this.dataGridView1.Columns[3].HeaderText = "Spalte 4";
      44. }
      45. private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
      46. {
      47. // Primär-Spalte
      48. int index = e.ColumnIndex;
      49. // Array, in dem die Reihenfolge der nachgeordneten Spalten festgelegt wird
      50. int[] sortOrder;
      51. switch (index)
      52. {
      53. case 0: // => default
      54. sortOrder = new int[] { 0, 1, 2, 3 };
      55. break;
      56. case 1:
      57. sortOrder = new int[] { 1, 2, 3, 0 };
      58. break;
      59. case 2:
      60. sortOrder = new int[] { 2, 3, 0, 1 };
      61. break;
      62. case 3:
      63. sortOrder = new int[] { 3, 0, 1, 2 };
      64. break;
      65. default:
      66. // Weitere Spalten: keine weiteren Kriterien
      67. sortOrder = new int[] { index };
      68. break;
      69. }
      70. // der erste Wert muss die selektierte Spalte sein
      71. System.Diagnostics.Debug.Assert(sortOrder != null && index == sortOrder[0], "Array falsch befüllt");
      72. Form1.OrderTable(this.dataGridView1, sortOrder);
      73. }
      74. /// <summary>
      75. /// Sortierung der DataTable eines DataGridView
      76. /// nach mehereren Kriterien
      77. /// </summary>
      78. /// <param name="dgv">das DataGridView</param>
      79. /// <param name="sortOrder">Reihenfolge der Kriterien</param>
      80. private static void OrderTable(DataGridView dgv, int[] sortOrder)
      81. {
      82. DataTable table = dgv.DataSource as DataTable;
      83. string order = Form1.GetOrderString(dgv, sortOrder[0]); // "ASC", "DESC"
      84. string sort = Form1.GetSortString(table, sortOrder, order); // "Color1 ASC, State ASC"
      85. Form1.Sort(table, sort);
      86. }
      87. /// <summary>
      88. /// Generieren des eigentlichen Sort-Strings
      89. /// </summary>
      90. /// <param name="table">Tabelle mit den Spaltennamen</param>
      91. /// <param name="sortOrder">Sortierabhängigkeit der Spalten untereinander</param>
      92. /// <param name="order">Sortierrichtung</param>
      93. /// <returns>der Sort-String</returns>
      94. private static string GetSortString(DataTable table, int[] sortOrder, string order)
      95. {
      96. // Spaltennamen in der Tabelle
      97. string sortString = string.Empty;
      98. // Länge begrenzen
      99. for (int i = 0; i < Math.Min(sortOrder.Length, table.Columns.Count); i++)
      100. {
      101. int elem = sortOrder[i];
      102. string name = table.Columns[elem].ColumnName;
      103. if (i < sortOrder.Length - 1)
      104. {
      105. sortString += string.Format("{0} {1}, ", name, order);
      106. }
      107. else
      108. {
      109. // der letzte String anders
      110. sortString += string.Format("{0} {1}", name, order);
      111. }
      112. }
      113. return sortString;
      114. }
      115. /// <summary>
      116. /// Generierung des Order-Strings anhand des DGV und des Spaltenindex'
      117. /// </summary>
      118. /// <param name="dgv">das zu sortierende DGV</param>
      119. /// <param name="index">der Spaltenindex</param>
      120. /// <returns>der Order-String</returns>
      121. private static string GetOrderString(DataGridView dgv, int index)
      122. {
      123. DataGridViewColumn oldColumn = dgv.SortedColumn;
      124. DataGridViewColumn newColumn = dgv.Columns[index];
      125. if (oldColumn == newColumn && dgv.SortOrder == SortOrder.Ascending)
      126. {
      127. // Sort the same column again, reversing the SortOrder.
      128. return "DESC";
      129. }
      130. // If oldColumn is null, then the DataGridView is not currently sorted.
      131. // Sort a new column
      132. return "ASC";
      133. }
      134. /// <summary>
      135. /// Sortieren einer DataTable
      136. /// </summary>
      137. /// <param name="table">die zu sortierende DataTable</param>
      138. /// <param name="sort">der Sort-String</param>
      139. private static void Sort(DataTable table, string sort)
      140. {
      141. // Create DataView
      142. DataView view = new DataView(table);
      143. // Sort DataView
      144. view.Sort = sort;
      145. // separate Instanz erforderlich, sonst ist die mit Clear() Tabelle weg
      146. DataTable table2 = view.ToTable();
      147. // Merge the sorted Table
      148. table.Clear();
      149. table.Merge(table2);
      150. table.AcceptChanges();
      151. }
      152. }
      153. }
      Zum Sortieren wird das DataGridView übergeben, um die letzte Sortierung auslesen zu können, so dass sich das Sortrieren wie das übliche Sortieren verhält.
      Die zu sortierende DataTable wird als DataSource aus dem DataGridView ausgelesen.
      --------------
      Die Testdaten sind so angelegt, dass immer die jeweils nächste Spalte die nächste Ordnung darstellt (0 - 1 - 2 - 3 - 0 - ...):
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ () aus folgendem Grund: Daten für zyklische Tauschung eingefügt

      Guck - ich mach sowas ähnliches über die BindingSource:

      VB.NET-Quellcode

      1. Imports System.Windows.Forms
      2. Imports System.ComponentModel
      3. Public Class frmHierarchicalSortTester
      4. Private WithEvents _Hierarchical As BindingSource
      5. Public Sub New()
      6. InitializeComponent()
      7. DataSet1.GenerateData()
      8. _Hierarchical = _Bs
      9. End Sub
      10. Private _RecentSorts() As String
      11. Private Sub Hirarchical_ListChanged(sender As Object, e As ListChangedEventArgs) Handles _Hierarchical.ListChanged
      12. If e.ListChangedType <> ListChangedType.Reset Then Return
      13. Dim dv = DirectCast(_Bs.List, DataView)
      14. Dim sort = dv.Sort ' Beispiel: dv.Sort = "[Zweiteiliger ColumnName] DESC"
      15. If sort = "" Then Return
      16. If _RecentSorts Is Nothing Then _RecentSorts = {sort, sort}
      17. Dim sortCol = sort.Split("]"c)(0) ' erste Sortierspalte ohne 'DESC'
      18. If sortCol <> _RecentSorts(0).Split("]"c)(0) Then _RecentSorts(1) = _RecentSorts(0) ' Recentliste ggfs. aufrücken
      19. _RecentSorts(0) = sort.Split(","c)(0) ' erste Sortierung - kann 'DESC' enthalten
      20. Dim DoubletteFree = _RecentSorts.Where(Function(s, i) i = 0 OrElse s.Split("]"c)(0) <> sortCol)
      21. sort = String.Join(", ", DoubletteFree)
      22. If dv.Sort <> sort Then dv.Sort = sort ' Beispiel neu: dv.Sort = "[Zweiteiliger ColumnName] DESC, [VorherigeColumn]"
      23. Diagnostics.Debug.Print(dv.Sort)
      24. End Sub
      25. End Class
      Ich habe mich aber auf maximal 2-stufige Sortierung festgelegt, noch mehr Stufen hab ich nie benötigt bzw. waren sogar im GUI ungünstig.
      Kann man aber sicher einfach erweitern.
      Die Funktionalität ist: Wenn man auf einen SpaltenHeader klickst, dann sortiert das DGV ja. Wenn man dann auf einen anderen klickst, dann sortiert es den, und übernimmt die vorherige Primär-Sortierung als sekundäre.
      War ziemliches Gefummel, weil man klickst ja auch denselben Header mehrmals, um dessen Sortierung umzudrehen - sowas soll die Sekundär-Sortierung ja beibehalten.
      Oder wenn mans mal auf 3 Spalten erweitert können Sort-Order-Dubletten entstehen, wenn man zw. 2 Spalten mehrmals hin-her - klickst.

      Ich verwende das übrigens für einen sehr interessanten m:n - View. Der kommt mit nur einem DGV aus, und ist quasi Umschaltbar von m->n zu n->m - naja so in Kürze schwer zu erklären.
      Ah - hier kann mans angugge: Dgv-Extensions

      Aber hier inne Anwendung verfolgt man die Korrektheit der Sort-Strings am einfachsten inne Debug-Ausgabe.
      An meinen Daten sieht mans zwar auch, aber da muss man genau gucken, ums zu erkennen.
      Dateien

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()