2011-01-10 447 views
8

我已經搜索了一個在多列上對DataGridView進行排序的示例,但似乎無法找到符合要求的示例。對多列進行DataGridView排序?

基本上,我有一個綁定的DataGridView控件(綁定到DataTable/DataView),綁定的DataTable有兩列: - 優先級和日期。我想按優先級排序。也就是說,優先級列優先,然後是日期,但都可以是升序或降序。

因此,舉例來說,我可以具有低優先級,早日第一(按優先級遞增順序,日期ASC),並且,通過單擊日期列標題,切換到低優先級,延遲日期第一(順序按優先順序排序,日期說明)。如果我然後點擊優先級,我想先優先,然後延遲日期(日期列的當前排序順序 - 按優先順序desc,日期desc),但然後能夠點擊日期列標題切換到高優先級,早日(按優先順序desc,日期升序)

理想情況下,我想對兩列的字形進行排序以顯示升序或降序。

任何想法或指針將受到感謝。

這個(看下面)看起來非常接近,但是這些字形並不正確。

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

對於箭頭字形而言,「不正常工作」是什麼意思?我真的不想花時間去編寫排序代碼,但是我已經看到了它的前景,而且現在看起來你已經是大部分了。 – 2011-01-10 13:35:41

+0

嗯......第一列的字形(優先級)在上下之間切換,但第二列的字形似乎是某種三態,並顯示爲上,無,無。我猜測DGV並不喜歡同時有兩個排序字形嗎?我添加了第三列「正常」排序,看起來很好,但(前兩列的字形消失),但單擊第二列的標題,然後在第一列給我一個升序字形。 – 2011-01-10 14:18:34

+0

Google for`MultisortDataGridView` IIRC。 – leppie 2011-01-10 15:20:35

回答

3

好的。

從科迪的建議上面,我現在有東西似乎按預期工作。我已經對HeaderCell進行了分類,並覆蓋了Paint方法(但通過在base.Paint之前立即設置SortGlyphDirection而被欺騙),並且DGV現在繪製多個排序字形。

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

我第一次讀這一點,我完全錯過了有關排序的部分由多個列同時(我的錯,不是你的;問題是非常清楚)。

如果是這樣的話,你將不得不自己編寫處理這些代碼。提供的DataGridView控件默認不支持多列排序。幸運的是,其他人已經做了很多工作來爲你實現這一點。這裏有幾樣:

另外,如果你綁定你DataGridView到數據源,這些數據源可以在多個列進行排序並且控制將遵循該排序。實現IBindingListView並公開Sort屬性的任何數據源都適用於多列排序。


但是,不管您選擇啓用多列排序的路線,你是不會有強迫的DataGridView上顯示多列排序箭頭字形很大的成功。這裏最簡單的解決方案是自定義繪製列標題以提供自己的排序字形。

爲此,請將處理程序附加到DataGridView.CellPainting event並檢查RowIndex(指示列標題)。有自己製作的列標題的完整樣本here。我強烈建議你堅持使用傳統的箭頭圖標,但一旦你走這條路線,這些選項就是無限的。您可以使列標題看起來像您想要的任何內容,甚至可以使用不同的圖標指示排序順序中每列的相對權重。

您也可以選擇從DataGridViewColumnHeaderCell派生出一個新類並覆蓋其Paint method。這可能是一個更清潔,更面向對象的完成同樣事情的方式。

+0

科迪,非常感謝您花時間提出解決方案 - 非常感謝。我使用HeaderCell/OnPaint方法,但也可能會調查您的其他建議。 – 2011-01-11 12:51:13

4

的DataGridView結合到它參考,到數據視圖所有情況下,數據源數據視圖,BindingSource的,表,數據集+ 「表名」)。獲取參考這個數據視圖,並設置排序(和過濾)如你所願:

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

注:排序和篩選使用列名對應列名的DataTable,在 列名DataGridView是用於綁定的基礎數據屬性名稱(類的屬性名稱,DataTables的列名稱等)。你可以在數據視圖中使用這樣的列名:

string colName = dgv.Columns[colIdx].DataPropertyName 

所依賴的你要如何跟蹤排序列(colSequence,COLNAME,遞增/遞減,dgvColIdx),你可以決定如何構建進行排序和篩選表達在dgv中設置SortGlyph(爲簡單起見,我製作了硬編碼)。

2

我從來沒有在這裏回答過問題,所以如果格式不正確,我很抱歉,但是我發現了這個問題的答案,對於未來的訪問者可能更簡單。 (請參閱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