2011-01-10 14 views
8

Szukałem przykładu sortowania DataGridView na wielu kolumnach, ale nie mogę znaleźć przykładu, który robi to, co chciałbym.Sortowanie DataGridView na wielu kolumnach?

Zasadniczo posiadam powiązaną kontrolkę DataGridView (powiązaną z DataTable/DataView), a związana DataTable ma dwie kolumny: - priorytet i datę. Chciałbym sortować według daty w ramach priorytetu. Oznacza to, że kolumna priorytetu ma pierwszeństwo, a następnie datę, ale obie mogą być rosnące lub malejące.

Tak więc, na przykład, może mam niski priorytet, wczesna data pierwszego (kolejność według priorytetu ASC data ASC), a klikając nagłówek data kolumny, przełącznik niskiego priorytetu, późna data pierwszej (kolejność według priorytetu asc, data desc). Jeśli następnie kliknę na priorytet, chciałbym najpierw mieć wysoki priorytet, a następnie późną datę (aktualny porządek sortowania dla kolumny daty - zamówienie według priorytetu desc, data desc), ale wtedy można kliknąć kolumnę daty nagłówek, aby przełączyć na wysoki priorytet, wczesną datę (kolejność według wzorca pierwszeństwa, daty asc).

Idealnie chciałbym posortować glify w obu kolumnach, aby pokazać rosnąco lub malejąco.

Wszelkie pomysły i wskazówki zostaną z wdzięcznością przyjęte.

Ten (patrz poniżej) wydaje się być bardzo blisko, ale glify nie działają poprawnie.

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

Co masz na myśli przez „prawo nie działa” w odniesieniu do glifów strzałek? Naprawdę nie chciałem poświęcać czasu na napisanie kodu do sortowania, ale dostałem to, aby wyglądało to wcześniej, i wygląda na to, że jesteś teraz na najlepszej drodze. –

+0

Cóż ... Glif dla pierwszej kolumny (priorytet) przełącza się pomiędzy górą a dół, ale glif dla drugiej kolumny wydaje się być czymś w rodzaju trzech stanów i wyświetla jako górny, nic, nic. Zgaduję, że DGV tak naprawdę nie lubi posiadania dwóch glifów sortujących w tym samym czasie? Dodałem trzecią kolumnę z "normalnym" sortowaniem i wydaje mi się to w porządku, ale (z glyfami na pierwszych dwóch kolumnach znikających), ale kliknięcie nagłówka drugiej kolumny daje mi wznoszący się glif w pierwszej kolumnie. –

+0

Google dla 'MultisortDataGridView' IIRC. – leppie

Odpowiedz

3

Ok.

Idąc za powyższymi sugestiami Cody'ego, mam teraz coś, co działa zgodnie z oczekiwaniami. Podklasowałem HeaderCell i przeskoczyłem metodę Paint (ale oszukiwałem, ustawiając SortGlyphDirection bezpośrednio przed bazą.Paint), a DGV teraz maluje wiele glifów sortujących.

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

Wrzuciłem rzeczy Linq dla zabawy, ale nie jestem przekonany, że to dobre podejście (zwłaszcza rozszerzenie "ForEach"). Myśli ? –

7

Pierwszy raz czytałem ten, całkowicie brakowało część o sortując wielokrotności kolumn jednocześnie (moja wina, nie twoja, pytanie było zupełnie jasne).

Jeśli tak jest, będziesz musiał napisać kod, który sam to zrobi. Podany kontrolka DataGridView nie obsługuje domyślnie sortowania wielokolumnowego. Na szczęście inni wykonali już wiele pracy, aby wdrożyć to za Ciebie. Oto kilka przykładów:

Ewentualnie, jeśli wiążą swoją DataGridView do źródła danych, że źródłem danych mogą być sortowane na wielu kolumnach i kontrolka DataGridView będzie szanować to sortowanie. Każde źródło danych, które implementuje IBindingListView i ujawnia właściwość Sort, będzie działać w przypadku sortowania wielokolumnowego.


Jednakże, niezależnie od drogi, który wybrać, aby umożliwić wielu sortowania kolumny, to nie będziemy mieć dużo sukcesów w zmuszaniu do DataGridView aby wyświetlić glif rodzaj strzałki na wielu kolumnach. Najłatwiejszym rozwiązaniem jest niestandardowe narysowanie tylko nagłówków kolumn, aby zapewnić własny glif sortowania.

Aby to zrobić, należy podłączyć program obsługi do DataGridView.CellPainting event i sprawdzić, czy jest to RowIndex z -1 (wskazujące nagłówek kolumny). Jest pełna próbka nagłówków kolumn rysowane przez właściciela here. Zdecydowanie zalecam trzymanie się konwencjonalnej ikony strzałki, ale po przejściu tej trasy opcje są naprawdę nieograniczone.Możesz sprawić, że nagłówki kolumn będą wyglądać tak, jak chcesz, a nawet wskazywać względną wagę każdej kolumny w porządku sortowania za pomocą różnych ikon.

Można także wybrać nową klasę z DataGridViewColumnHeaderCell i zastąpić jej Paint method. Jest to prawdopodobnie czystszy, bardziej obiektowy sposób na osiągnięcie tego samego.

+0

Cody, Wielkie dzięki za poświęcenie czasu na zaproponowanie rozwiązania - jest to bardzo cenne. Poszedłem z metodą HeaderCell/OnPaint, ale mogę również zbadać inne twoje sugestie. –

4

Gdy DataGridView wiąże się źródła danych (DataView, BindingSource, tabeli zestaw danych + "tablename") we wszystkich przypadkach referen do DataView. Uzyskać odniesienie do tego DataView i ustawić Sortuj (i Filter), jak chcesz:

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; 
} 

Uwaga: nazwy kolumna w sortowanie i filtrowanie odpowiadają nazwom kolumn w DataTable, Nazwy kolumn w DataGridView to podstawowe nazwy właściwości danych używane do wiązania (nazwy właściwości dla klas, nazwy kolumn dla DataTables itp.). Można uzyskać nazwę kolumny używane w DataView tak:

string colName = dgv.Columns[colIdx].DataPropertyName 

zależy od jej jak chcesz śledzić posortowane kolumny (colSequence, colName, asc/desc, dgvColIdx) może zdecydować, w jaki sposób budować sortowanie i filtrowanie ekspresji i ustaw SortGlyph w dgv (zrobiłem hardcode dla uproszczenia).

2

Nigdy nie odpowiedziałem na pytanie tutaj, więc przepraszam, jeśli format jest niepoprawny, ale znalazłem odpowiedź na to pytanie, które może być prostsze dla przyszłych użytkowników. (Patrz 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

Czy możesz wyjaśnić, dlaczego to pomoże Black Light z ich problemem? –

+1

Cóż, zrozumiałem, że pytanie Czarnego Światła polega na tym, że chcą sortować pierwotnie i wtórnie na kolumnach, które są powiązane z danymi, a następnie mieć glify na każdym nagłówku, który mówi, w którym kierunku jest sortowany.Ten kod daje pierwszeństwo w kolejności sortowania kolumn, a ostatnia kolumna ma być traktowana priorytetowo. Pokazuje także glify kierunkowe na każdej kolumnie. – Mh4

Powiązane problemy