2011-01-10 24 views
8

Ich habe nach einem Beispiel für die Sortierung eines DataGridView auf mehrere Spalten gesucht, aber ich finde nicht in der Lage, ein Beispiel zu finden, das tut, was ich möchte.Eine DataGridView auf mehreren Spalten sortieren?

Grundsätzlich habe ich ein gebundenes DataGridView-Steuerelement (an eine DataTable/DataView gebunden), und die gebundene DataTable hat zwei Spalten: - Priorität und Datum. Ich möchte nach Priorität sortieren. Das heißt, die Prioritätsspalte hat Vorrang, dann ist es das Datum, aber beide können aufsteigend oder absteigend sein.

So zum Beispiel kann ich mit niedriger Priorität, früh erste (Reihenfolge nach Priorität asc, Datum auf) und durch den Datum Spaltenkopf, Schalter auf niedrige Priorität klicken, spätes Datum zuerst (Bestellung nach Priorität Asc, Datum desc). Wenn ich dann auf die Priorität klicke, möchte ich zuerst eine hohe Priorität haben, dann ein spätes Datum (die aktuelle Sortierreihenfolge für die Datumsspalte - Reihenfolge nach Priorität desc, Datum desc), aber dann in der Lage sein, auf die Datumsspalte zu klicken Kopfzeile, um zu hoher Priorität zu wechseln, frühes Datum (Reihenfolge nach Priorität desc, Datum asc).

Idealerweise möchte ich Glyphen für beide Spalten sortieren, um aufsteigend oder absteigend anzuzeigen.

Alle Ideen oder Hinweise würden dankbar erhalten werden.

Dies (siehe unten) scheint ziemlich nah zu sein, aber die Glyphen funktionieren nicht richtig.

using System; 
using System.Windows.Forms; 

namespace WindowsFormsApplication4 
{ 
    public partial class Form1 : Form 
    { 
    DataSet1 dataset; 

    public Form1() 
    { 
     InitializeComponent(); 

     dataset = new DataSet1(); // two columns: Priority(Int32) and date (DateTime) 
     dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("01-jan-10")); 
     dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("02-jan-10")); 
     dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("03-jan-10")); 
     dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("04-jan-10")); 
     dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("05-jan-10")); 
     dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("06-jan-10")); 
     dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("07-jan-10")); 
     dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("08-jan-10")); 
     dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("09-jan-10")); 

     dataGridView1.DataSource = dataset.DataTable1.DefaultView; 

     dataGridView1.AllowUserToAddRows = false; 

     dataGridView1.Columns[0].SortMode = DataGridViewColumnSortMode.Programmatic; 
     dataGridView1.Columns[1].SortMode = DataGridViewColumnSortMode.Programmatic; 

     dataGridView1.Columns[0].HeaderCell.SortGlyphDirection = SortOrder.Ascending; 
     dataGridView1.Columns[1].HeaderCell.SortGlyphDirection = SortOrder.Ascending; 
    } 

    private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) 
    { 
     DataGridViewColumn[] column = new[] { dataGridView1.Columns[0], dataGridView1.Columns[1] }; 

     DataGridViewColumnHeaderCell headerCell = dataGridView1.Columns[e.ColumnIndex].HeaderCell; 

     if (headerCell.SortGlyphDirection != SortOrder.Ascending) 
      headerCell.SortGlyphDirection = SortOrder.Ascending; 
     else 
      headerCell.SortGlyphDirection = SortOrder.Descending; 

     String sort = column[0].DataPropertyName + " " + fnSortDirection(column[0]) 
        + ", " 
        + column[1].DataPropertyName + " " + fnSortDirection(column[1]); 
     dataset.DataTable1.DefaultView.Sort = sort; 
     this.textBox1.Text = sort; 
    } 

    private String fnSortDirection(DataGridViewColumn column) 
    { 
     return column.HeaderCell.SortGlyphDirection != SortOrder.Descending ? "asc" : "desc"; 
    } 
    } 
} 
+0

Was meinen Sie mit "funktionieren nicht richtig" in Bezug auf die Pfeilzeichen? Ich wollte mir wirklich nicht die Zeit nehmen, um den Sortiercode zu schreiben, aber ich habe das vorher genau gesehen, und es sieht so aus, als ob du jetzt am meisten da bist. –

+0

Nun ... die Glyphe für die erste Spalte (Priorität) wechselt zwischen oben und unten, aber die Glyphe für die zweite Spalte scheint eine Art Dreizustand zu sein und wird als hoch, nichts, nichts angezeigt. Ich nehme an, dass der DGV nicht wirklich mag, zwei Glyphen gleichzeitig zu sortieren? Ich habe eine dritte Spalte mit "normaler" Sortierung hinzugefügt, und das scheint in Ordnung zu sein (die Glyphen in den ersten beiden Spalten verschwinden jedoch), aber wenn ich auf die Überschrift für die zweite Spalte klicke, erscheint eine aufsteigende Glyphe in der ersten Spalte. –

+0

Google für 'MultisortDataGridView' IIRC. – leppie

Antwort

3

Ok.

Nach Codys obigen Vorschlägen habe ich jetzt etwas, das wie erwartet funktioniert. Ich habe die HeaderCell unterklassifiziert und die Paint-Methode übersteuert (aber umgangen, indem ich die SortGlyphDirection unmittelbar vor der base.Paint gesetzt habe) und der DGV malt nun mehrere Sortierzeichen.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Windows.Forms; 

namespace WindowsFormsApplication4 
{ 
    public partial class Form1 : Form 
    { 
    DataSet1 dataset; 

    public Form1() 
    { 
     InitializeComponent(); 

     dataset = new DataSet1(); // three columns: Priority(Int32), Date (DateTime) and Description(String) 
     dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("01-jan-10"), "this"); 
     dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("02-jan-10"), "is"); 
     dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("03-jan-10"), "a"); 
     dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("04-jan-10"), "sample"); 
     dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("05-jan-10"), "of"); 
     dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("06-jan-10"), "the"); 
     dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("07-jan-10"), "data"); 
     dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("08-jan-10"), "in"); 
     dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("09-jan-10"), "use"); 

     dataGridView1.DataSource = dataset.DataTable1.DefaultView; 

     dataGridView1.AllowUserToAddRows = false; 

     dataGridView1.Columns[0].HeaderCell = new MyDataGridViewColumnHeaderCell(); 
     dataGridView1.Columns[1].HeaderCell = new MyDataGridViewColumnHeaderCell(); 

     dataGridView1.Columns[0].SortMode = DataGridViewColumnSortMode.Programmatic; 
     dataGridView1.Columns[1].SortMode = DataGridViewColumnSortMode.Programmatic; 
    } 

    private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) 
    { 
     DataGridViewColumn clickedColumn = dataGridView1.Columns[e.ColumnIndex]; 

     if (clickedColumn.HeaderCell is MyDataGridViewColumnHeaderCell) 
     { 
      DoMultiColumnSort(); 
     } 
     else 
     { 
      dataGridView1.Columns.OfType<DataGridViewColumn>() 
           .Where(column => column.HeaderCell is MyDataGridViewColumnHeaderCell) 
           .ForEach(column => ((MyDataGridViewColumnHeaderCell)column.HeaderCell).SortOrderDirection = SortOrder.None); 
     } 

     this.textBox1.Text = dataset.DataTable1.DefaultView.Sort; 
    } 

    private void DoMultiColumnSort() 
    { 
     var sortClauses = dataGridView1.Columns.OfType<DataGridViewColumn>() 
               .Where(column => column.HeaderCell is MyDataGridViewColumnHeaderCell) 
               .Select(column => GetSortClause(column)); 

     dataset.DataTable1.DefaultView.Sort = String.Join(",", sortClauses); 
    } 

    private String GetSortClause(DataGridViewColumn column) 
    { 
     SortOrder direction = column.HeaderCell.SortGlyphDirection; 

     if (column.HeaderCell is MyDataGridViewColumnHeaderCell) 
     { 
      direction = ((MyDataGridViewColumnHeaderCell)column.HeaderCell).SortOrderDirection; 
     } 

     return column.DataPropertyName + " " + (direction == SortOrder.Descending ? "DESC" : "ASC"); 
    } 
    } 

    public partial class MyDataGridViewColumnHeaderCell : DataGridViewColumnHeaderCell 
    { 
    public SortOrder SortOrderDirection { get; set; } // defaults to zero = SortOrder.None; 

    protected override void Paint(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle cellBounds, int rowIndex, DataGridViewElementStates dataGridViewElementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) 
    { 
     this.SortGlyphDirection = this.SortOrderDirection; 
     base.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts); 
    } 

    public override object Clone() 
    { 
     MyDataGridViewColumnHeaderCell result = (MyDataGridViewColumnHeaderCell)base.Clone(); 
     result.SortOrderDirection = this.SortOrderDirection; 
     return result; 
    } 

    protected override void OnClick(DataGridViewCellEventArgs e) 
    { 
     this.SortOrderDirection = (this.SortOrderDirection != SortOrder.Ascending) ? SortOrder.Ascending : SortOrder.Descending; 
     base.OnClick(e); 
    } 
    } 

    public static partial class Extensions 
    { 
    public static void ForEach<T>(this IEnumerable<T> value, Action<T> action) { foreach (T item in value) { action(item); } } 
    } 
} 
+0

Ich habe den Linq-Kram nur zum Spaß hinzugefügt, aber ich bin nicht davon überzeugt, dass es ein guter Ansatz ist (besonders die "ForEach" -Erweiterung). Gedanken ? –

7

Das erste Mal, dass ich diese Zeilen lesen, ich völlig verfehlt das Teil Spalten gleichzeitig durch mehrere über das Sortieren (meine Schuld, nicht verkaufen, die Frage ganz klar war).

Wenn das der Fall ist, müssen Sie den Code schreiben, der das selbst handhabt. Das bereitgestellte Steuerelement DataGridView unterstützt standardmäßig keine Sortierung nach mehreren Spalten. Zum Glück haben andere bereits viel getan, um dies für Sie umzusetzen. Hier sind ein paar Beispiele:

Alternativ, wenn Sie Ihre DataGridView an eine Datenquelle binden, kann diese Datenquelle auf mehrere Spalten sortiert werden und die DataGridView Steuerung wird diese Sortierung berücksichtigen. Jede Datenquelle, die IBindingListView implementiert und eine Sort-Eigenschaft verfügbar macht, funktioniert für die Sortierung mit mehreren Spalten.


jedoch unabhängig von der Route, die Sie wählen Mehrspaltensortierung zu ermöglichen, werden Sie nicht viel Erfolg haben in den DataGridView Nötigung der Sortierpfeil Glyph auf mehrere Spalten angezeigt werden soll. Die einfachste Lösung besteht darin, nur die Spaltenüberschriften benutzerdefiniert zu zeichnen, um ein eigenes Sortierzeichen zu erhalten.

Um dies zu tun, fügen Sie einen Handler an die DataGridView.CellPainting event und überprüfen Sie eine RowIndex von -1 (Angabe einer Spaltenüberschrift). Es gibt eine vollständige Stichprobe von vom Besitzer gezeichneten Spaltenüberschriften here. Ich empfehle dringend, mit dem herkömmlichen Pfeil-Symbol zu bleiben, aber sobald Sie diesen Weg gehen, sind die Möglichkeiten wirklich unbegrenzt.Sie können Ihre Spaltenüberschriften wie gewünscht aussehen lassen und sogar das relative Gewicht jeder Spalte in der Sortierreihenfolge mithilfe verschiedener Symbole angeben. Sie können auch eine neue Klasse von DataGridViewColumnHeaderCell ableiten und die Paint method überschreiben. Dies ist wahrscheinlich eine sauberere, objektorientiertere Art, dasselbe zu erreichen.

+0

Cody, vielen Dank, dass Sie sich die Zeit genommen haben, eine Lösung vorzuschlagen - es wird sehr geschätzt. Ich ging mit der HeaderCell/OnPaint-Methode, könnte aber auch Ihre anderen Vorschläge untersuchen. –

4

Wenn Datagridview an die Datasource bindet (Dataview, Binding, Tisch, DataSet + "Tabellenname") in allen Fällen zum Dataview refere. Erhalten Sie Hinweis auf diese Dataview und stellen Sortieren (und Filter), wie Sie wollen:

DataView dv = null; 
CurrencyManager cm = (CurrencyManager)(dgv.BindingContext[dgv.DataSource, dgv.DataMember]); 

if (cm.List is BindingSource) 
{ 
    // In case of BindingSource it may be chain of BindingSources+relations 
    BindingSource bs = (BindingSource)cm.List; 
    while (bs.List is BindingSource) 
    { bs = bs.List as BindingSource; } 

    if (bs.List is DataView) 
    { dv = bs.List as DataView; } 
} 
else if (cm.List is DataView) 
{ 
    // dgv bind to the DataView, Table or DataSet+"tablename" 
    dv = cm.List as DataView; 
} 

if (dv != null) 
{ 
    dv.Sort = "somedate desc, firstname"; 
    // dv.Filter = "lastname = 'Smith' OR lastname = 'Doe'"; 

    // You can Set the Glyphs something like this: 
    int somedateColIdx = 5; // somedate 
    int firstnameColIdx = 3; // firstname 
    dgv.Columns[somedateColIdx].HeaderCell.SortGlyphDirection = SortOrder.Descending; 
    dgv.Columns[firstnameColIdx].HeaderCell.SortGlyphDirection = SortOrder.Ascending; 
} 

Hinweis: Spaltennamen verwendet, in Sortier- und Filter entsprechen den Spaltennamen in Datatable, Spaltennamen in DataGridView sind die zugrunde liegenden Dateneigenschaftsnamen, die für die Bindung verwendet werden (Eigenschaftsnamen für Klassen, Spaltennamen für DataTables usw.). Sie können Spaltennamen in Dataview so benutzten erhalten:

string colName = dgv.Columns[colIdx].DataPropertyName 

Hängt davon, wie wollen Sie sortiert Spalten (colSequence, colName, asc/ab, dgvColIdx) Sie können beschließen, verfolgen, wie Sortieren zu bauen und filtern Ausdruck und setze das SortGlyph in dgv (ich habe Hardcode zur Vereinfachung gemacht).

2

Ich habe nie eine Frage hier beantwortet, also entschuldige ich mich, wenn das Format falsch ist, aber ich fand eine Antwort auf diese Frage, die für zukünftige Besucher einfacher sein kann. (Siehe http://www.pcreview.co.uk/threads/datagridview-glyphs.3145090/)

Dim dictionarySortColumns As New Dictionary(Of String, Integer) 


Private Sub DataGridViewFileLoader_Sorted(sender As Object, e As EventArgs) Handles DataGridViewFileLoader.Sorted 


    Dim dv As New DataView(dataSetLoadScreener.Tables(0)) 
    Dim columnHeader As String = DataGridViewFileLoader.SortedColumn.Name 

    Dim sortDirection As Integer = DataGridViewFileLoader.SortOrder 
    Dim sortcode As String = "" 
    Dim sortOrder As String = "" 

    If sortDirection = 1 Then 
     sortOrder = "ASC" 
    Else 
     sortOrder = "DESC" 
    End If 

    If dictionarySortColumns.ContainsKey(columnHeader) Then 
     dictionarySortColumns.Remove(columnHeader) 
    End If 

    sortcode = columnHeader + " " + sortOrder 

    For Each colHeader As String In dictionarySortColumns.Keys 
     If dictionarySortColumns(colHeader) = 1 Then 
      sortOrder = "ASC" 
     Else 
      sortOrder = "DESC" 
     End If 

     sortcode = sortcode + "," + colHeader + " " + sortOrder 

    Next 

    dictionarySortColumns.Add(columnHeader, sortDirection) 

    dv.Sort = sortcode 
    DataGridViewFileLoader.DataSource = Nothing 
    DataGridViewFileLoader.DataSource = dv 

    formatDataGridViewFileLoader() 

End Sub 

Private Sub DataGridViewFileLoader_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles DataGridViewFileLoader.CellPainting 
    Dim sOrder As System.Windows.Forms.SortOrder 

    For Each key As String In dictionarySortColumns.Keys 
     If dictionarySortColumns(key) = 1 Then 
      sOrder = Windows.Forms.SortOrder.Ascending 
     Else 
      sOrder = Windows.Forms.SortOrder.Descending 
     End If 

     DataGridViewFileLoader.Columns(key).HeaderCell.SortGlyphDirection = sOrder 
    Next 
End Sub 
+0

Kannst du bitte erklären, warum dies Black Light bei ihrem Problem helfen würde? –

+1

Nun, ich habe die Frage von Black Light verstanden, dass sie eine primäre und sekundäre Sortierung für Spalten haben möchten, die an eine Datenansicht gebunden sind, und dann Glyphen auf jeder Kopfzeile haben, die angibt, in welche Richtung sie sortiert.Dieser Code gibt die Priorität in der Reihenfolge an, in der die Spalten sortiert sind, wobei die letzte zu sortierende Spalte die höchste Priorität hat. Es zeigt auch Richtungszeichen in jeder Spalte. – Mh4

Verwandte Themen