2011-01-10 66 views
8

He buscado un ejemplo para ordenar un DataGridView en varias columnas, pero parece que no puedo encontrar un ejemplo que haga lo que me gustaría.¿Ordenar un DataGridView en varias columnas?

Básicamente, tengo un control DataGridView vinculado (vinculado a un DataTable/DataView), y el DataTable encuadernado tiene dos columnas: - prioridad y fecha. Me gustaría ordenar por fecha con prioridad. Es decir, la columna de prioridad toma precendencia, luego es la fecha, pero ambas pueden ser ascendentes o descendentes.

Así, por ejemplo, es posible que tenga baja prioridad, fecha temprana primera (ordenado por asc prioridad, fecha asc) y, haciendo clic en el encabezado de la columna Fecha, cambie a baja prioridad, la fecha final de la primera (orden por prioridad asc, fecha desc). Si hago clic en la prioridad, me gustaría tener prioridad alta primero, luego fecha tardía (el orden de clasificación actual para la columna de fecha - ordena por prioridad desc, fecha desc), pero luego, puede hacer clic en la columna de fecha encabezado para cambiar a alta prioridad, fecha temprana (orden por prioridad desc, fecha asc).

Idealmente, me gustaría ordenar los glifos en ambas columnas para mostrar ascendente o descendente.

Cualquier idea o sugerencia sería gratamente recibida.

Esto (ver más abajo) parece acercarse bastante, pero los glifos no funcionan bien.

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

¿Qué quiere decir con "no están funcionando bien" con respecto a los glifos de flecha? Realmente no quería tomarme el tiempo para escribir el código de clasificación, pero he conseguido que esto se vea justo antes, y parece que ya has recorrido la mayor parte del camino. –

+0

Bueno ... el glifo de la primera columna (prioridad) alterna entre arriba y abajo, pero el glifo de la segunda columna parece ser un tipo de tres estados, y se visualiza como arriba, nada, nada. Supongo que a la DGV no le gusta tener dos glifos de clasificación al mismo tiempo. Agregué una tercera columna con clasificación "normal", y eso parece estar bien pero (con los glifos en las dos primeras columnas desapareciendo), pero al hacer clic en el encabezado de la segunda columna, aparece un glifo ascendente en la primera columna. –

+0

Google for 'MultisortDataGridView' IIRC. – leppie

Respuesta

3

Ok.

Siguiendo las sugerencias anteriores de Cody, ahora tengo algo que parece funcionar como se esperaba. He subclasificado el HeaderCell y he anulado el método Paint (pero me he defraudado al establecer SortGlyphDirection inmediatamente antes del base.Paint) y el DGV ahora pinta múltiples glifos de clasificación.

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

Lancé las cosas de Linq solo por diversión, pero no estoy convencido de que sea un buen enfoque (especialmente la extensión "ForEach"). ¿Pensamientos? –

7

La primera vez que leí esto, se perdió totalmente la parte de la clasificación por múltiples columnas al mismo tiempo (mi culpa, no la tuya; la cuestión era perfectamente claro).

Si ese es el caso, tendrá que escribir el código que maneja esto usted mismo. El control proporcionado DataGridView no admite la clasificación de múltiples columnas de forma predeterminada. Afortunadamente, otros ya han hecho gran parte del trabajo para implementar esto para usted. Aquí están algunos ejemplos:

Alternativamente, si enlaza su DataGridView a una fuente de datos, esa fuente de datos se pueden clasificar en varias columnas y el control DataGridView respetará esa clasificación. Cualquier fuente de datos que implemente IBindingListView y expone una propiedad Sort funcionará para la clasificación de múltiples columnas.


Sin embargo, independientemente de la ruta que elija para permitir la clasificación de varias columnas, usted no va a tener mucho éxito en forzar a la DataGridView para mostrar el glifo tipo flecha en varias columnas. La solución más fácil aquí es dibujar a medida solo los encabezados de columna para proporcionar su propio glifo de ordenación.

Para ello, adjunte un controlador al DataGridView.CellPainting event y compruebe si hay RowIndex de -1 (lo que indica un encabezado de columna). Hay una muestra completa de encabezados de columnas dibujadas por el propietario here. Recomiendo seguir con el icono de flecha convencional, pero una vez que va por esta ruta, las opciones son realmente ilimitadas.Puede hacer que los encabezados de sus columnas se vean como lo desee, e incluso indicar el peso relativo de cada columna en el orden de clasificación con diferentes iconos.

También puede elegir derivar una nueva clase de DataGridViewColumnHeaderCell y anular su Paint method. Esta es probablemente una forma más limpia y orientada a objetos de lograr lo mismo.

+0

Cody, Muchas gracias por tomarse el tiempo de sugerir una solución, es muy apreciada. Fui con el método HeaderCell/OnPaint, pero también podría investigar sus otras sugerencias. –

4

Cuando DataGridView se une a la DataSource (DataView, BindingSource, Mesa, DataSet + "nombre de tabla") en todos los casos REFERE a la DataView. Obtener referencia a la presente DataView y establecer Ordenar (y Filtro) como desee:

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

Nota: Los nombres columna utilizada en Ordenar y filtrar corresponden a los nombres de las columnas de DataTable, nombres de columna de DataGridView son los nombres de propiedad de datos subyacentes utilizados para el enlace (nombres de propiedad para clases, nombres de columna para DataTables, etc.). Se puede obtener el nombre de la columna utilizada en DataView como esto:

string colName = dgv.Columns[colIdx].DataPropertyName 

Depende de cómo usted desea realizar un seguimiento columnas ordenados (colSequence, nomCol, asc/desc, dgvColIdx) se puede decidir cómo construir ordenar y filtrar expresión y establezca SortGlyph en dgv (hice hardcode para simplificar).

2

Nunca he respondido una pregunta aquí, entonces me disculpo si el formato es incorrecto, pero encontré una respuesta a esta pregunta que puede ser más sencilla para los visitantes futuros. (Ver 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

¿Puedes explicar por qué esto ayudaría a Black Light a resolver su problema? –

+1

bien, entendí que la pregunta de Black Light es que quieren tener clasificación primaria y secundaria en columnas que están vinculadas a una vista de datos, y luego tener glifos en cada encabezado que indica en qué dirección está ordenando.Este código da prioridad en el orden en que se ordenan las columnas, con la columna más reciente ordenada con la mayor prioridad. También muestra glifos de dirección en cada columna. – Mh4