2008-11-11 12 views
5

yo pongo en marcha mi DataGridView como esto:DataGridView Columna de clasificación con Business Objects

 jobs = new List<DisplayJob>(); 

     uxJobList.AutoGenerateColumns = false; 
     jobListBindingSource.DataSource = jobs; 
     uxJobList.DataSource = jobListBindingSource; 

     int newColumn; 
     newColumn = uxJobList.Columns.Add("Id", "Job No."); 
     uxJobList.Columns[newColumn].DataPropertyName = "Id"; 
     uxJobList.Columns[newColumn].DefaultCellStyle.Format = Global.JobIdFormat; 
     uxJobList.Columns[newColumn].DefaultCellStyle.Font = new Font(uxJobList.DefaultCellStyle.Font, FontStyle.Bold); 
     uxJobList.Columns[newColumn].AutoSizeMode = DataGridViewAutoSizeColumnMode.None; 
     uxJobList.Columns[newColumn].Width = 62; 
     uxJobList.Columns[newColumn].Resizable = DataGridViewTriState.False; 
     uxJobList.Columns[newColumn].SortMode = DataGridViewColumnSortMode.Automatic; 
     : 
     : 

donde la clase DisplayJob parece:

public class DisplayJob 
{ 
    public DisplayJob(int id) 
    { 
     Id = id; 
    } 

    public DisplayJob(JobEntity job) 
    { 
     Id = job.Id; 
     Type = job.JobTypeDescription; 
     CreatedAt = job.CreatedAt; 
     StartedAt = job.StartedAt; 
     ExternalStatus = job.ExternalStatus; 
     FriendlyExternalStatus = job.FriendlyExternalStatus; 
     ExternalStatusFriendly = job.ExternalStatusFriendly; 
     CustomerName = job.Customer.Name; 
     CustomerKey = job.Customer.CustomerKey; 
     WorkAddress = job.WorkAddress; 
     CreatedBy = job.CreatedBy; 
     CancelledAt = job.CancelledAt; 
     ClosedAt = job.ClosedAt; 
     ReasonWaiting = job.ReasonWaiting; 
     CancelledBy = job.CancelledBy; 
     CancelledReason = job.CancelledReason; 
     DisplayCreator = Global.GetDisplayName(CreatedBy); 
     ActionRedoNeeded = job.ActionRedoNeeded; 
     if (job.Scheme != null) 
     { 
      SchemeCode = job.Scheme.Code; 
     } 

    } 

    public int Id { get; private set; } 
    public string Type { get; private set; } 
    public DateTime CreatedAt { get; private set; } 
    public DateTime? StartedAt { get; private set; } 
    public string ExternalStatus { get; private set; } 
    public string FriendlyExternalStatus { get; private set; } 
    public string ExternalStatusFriendly { get; private set; } 
    public string CustomerName { get; private set; } 
    public string CustomerKey { get; private set; } 
    public string WorkAddress { get; private set; } 
    public string CreatedBy { get; private set; } 
    public DateTime? CancelledAt { get; private set; } 
    public DateTime? ClosedAt { get; private set; } 
    public string CancelledBy { get; private set; } 
    public string ReasonWaiting { get; private set; } 
    public string DisplayCreator { get; private set; } 
    public string CancelledReason { get; private set; } 
    public string SchemeCode { get; private set; } 
    public bool ActionRedoNeeded { get; private set; } 
} 

Sin embargo, la columna de clasificación no funciona. ¿Cuál es la mejor manera de hacer que esto funcione?

Respuesta

9

Si desea admitir la ordenación y búsqueda en la colección, todos lo toman para derivar una clase de su tipo parametrizado BindingList, y anular algunos métodos y propiedades de la clase base.

La mejor manera es extender el BindingList y hacemos las siguientes cosas:

protected override bool SupportsSearchingCore 
{ 
    get 
    { 
     return true; 
    } 
} 

protected override bool SupportsSortingCore 
{ 
    get { return true; } 
} 

También tendrá que implementar el código para ordenar:

ListSortDirection sortDirectionValue; 
PropertyDescriptor sortPropertyValue; 

protected override void ApplySortCore(PropertyDescriptor prop, 
    ListSortDirection direction) 
{ 
    sortedList = new ArrayList(); 

    // Check to see if the property type we are sorting by implements 
    // the IComparable interface. 
    Type interfaceType = prop.PropertyType.GetInterface("IComparable"); 

    if (interfaceType != null) 
    { 
     // If so, set the SortPropertyValue and SortDirectionValue. 
     sortPropertyValue = prop; 
     sortDirectionValue = direction; 

     unsortedItems = new ArrayList(this.Count); 

     // Loop through each item, adding it the the sortedItems ArrayList. 
     foreach (Object item in this.Items) { 
      sortedList.Add(prop.GetValue(item)); 
      unsortedItems.Add(item); 
     } 
     // Call Sort on the ArrayList. 
     sortedList.Sort(); 
     T temp; 

     // Check the sort direction and then copy the sorted items 
     // back into the list. 
     if (direction == ListSortDirection.Descending) 
      sortedList.Reverse(); 

     for (int i = 0; i < this.Count; i++) 
     { 
      int position = Find(prop.Name, sortedList[i]); 
      if (position != i) { 
       temp = this[i]; 
       this[i] = this[position]; 
       this[position] = temp; 
      } 
     } 

     isSortedValue = true; 

     // Raise the ListChanged event so bound controls refresh their 
     // values. 
     OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); 
    } 
    else 
     // If the property type does not implement IComparable, let the user 
     // know. 
     throw new NotSupportedException("Cannot sort by " + prop.Name + 
      ". This" + prop.PropertyType.ToString() + 
      " does not implement IComparable"); 
} 

Si necesita más información siempre se puede ir allí y obtener toda la explicación sobre how to extend the binding list.

1

Creo que su clase debe implementar la interfaz IComparable.

creo que sirve,

Bruno Figueiredo

3

Una de las maneras más fáciles es utilizar la clase BindingListView para envolver su lista de DisplayJobs. La clase implementa algunas de las interfaces requeridas que permiten ordenar y filtrar en un DataGridView. Esa es la manera rápida. Sin embargo, funciona bastante bien: la única advertencia es que si extrae cosas del DataGridView, debe convertirlo al objeto contenedor (ObjectView) en lugar del elemento real (DisplayJob).

La manera menos lenta es crear un tiempo de recopilación personalizado que implemente IBindingList, implementando allí los métodos de ordenación.

+0

BindingListView está convenientemente disponible a través de Nuget: Instalar Paquete Unofficial.BindingListView – user1016736

5

La solución de Daok es la correcta. También suele ser más trabajo de lo que vale.

La manera del hombre perezoso de obtener la funcionalidad que desea es crear y completar una DataTable fuera de sus objetos comerciales y vincular DataGridView a eso.

Hay muchos casos de uso que este enfoque no manejará (como, edición) y, obviamente, desperdicia tiempo y espacio. Como dije, es flojo.

Pero es fácil de escribir, y el código resultante es muchísimo menos misterioso que una implementación de IBindingList.

Además, ya está escribiendo mucho código, o código similar al menos: el código que escribe para definir DataTable le libera de tener que escribir código para crear las columnas de DataGridView, ya que DataGridView construirá sus columnas fuera del DataTable cuando lo vincule.

3

El artículo de MS sugerido por Daok me puso en el camino correcto, pero no estaba satisfecho con la implementación de MS de SortableSearchableList. Encuentro esa implementación muy extraña y no funcionó bien cuando hay valores duplicados en una columna.Tampoco anula IsSortedCore, que parece ser requerido por DataGridView. Si IsSortedCore no se anula, el glifo de búsqueda no aparece y alternar entre ascendente y descendente no funciona.

Consulte mi versión de SortableSearchableList a continuación. En ApplySortCore() ordena usando un delegado de comparación establecido en un método anónimo. Esta versión también admite la configuración de comparaciones personalizadas para una propiedad en particular, que puede agregarse mediante una clase derivada utilizando AddCustomCompare().

No estoy seguro de si el aviso de copyright sigue siendo válida, pero me acaba de dejar en.

//--------------------------------------------------------------------- 
// Copyright (C) Microsoft Corporation. All rights reserved. 
// 
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY 
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
//PARTICULAR PURPOSE. 
//--------------------------------------------------------------------- 

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 
using System.Reflection; 
using System.Collections; 

namespace SomethingSomething 
{ 
    /// <summary> 
    /// Supports sorting of list in data grid view. 
    /// </summary> 
    /// <typeparam name="T">Type of object to be displayed in data grid view.</typeparam> 
    public class SortableSearchableList<T> : BindingList<T> 
    { 
     #region Data Members 

     private ListSortDirection _sortDirectionValue; 
     private PropertyDescriptor _sortPropertyValue = null; 

     /// <summary> 
     /// Dictionary from property name to custom comparison function. 
     /// </summary> 
     private Dictionary<string, Comparison<T>> _customComparisons = new Dictionary<string, Comparison<T>>(); 

     #endregion 

     #region Constructors 

     /// <summary> 
     /// Default constructor. 
     /// </summary> 
     public SortableSearchableList() 
     { 
     } 

     #endregion 

     #region Properties 

     /// <summary> 
     /// Indicates if sorting is supported. 
     /// </summary> 
     protected override bool SupportsSortingCore 
     { 
      get 
      { 
       return true; 
      } 
     } 

     /// <summary> 
     /// Indicates if list is sorted. 
     /// </summary> 
     protected override bool IsSortedCore 
     { 
      get 
      { 
       return _sortPropertyValue != null; 
      } 
     } 

     /// <summary> 
     /// Indicates which property the list is sorted. 
     /// </summary> 
     protected override PropertyDescriptor SortPropertyCore 
     { 
      get 
      { 
       return _sortPropertyValue; 
      } 
     } 

     /// <summary> 
     /// Indicates in which direction the list is sorted on. 
     /// </summary> 
     protected override ListSortDirection SortDirectionCore 
     { 
      get 
      { 
       return _sortDirectionValue; 
      } 
     } 

     #endregion 

     #region Methods  

     /// <summary> 
     /// Add custom compare method for property. 
     /// </summary> 
     /// <param name="propertyName"></param> 
     /// <param name="compareProperty"></param> 
     protected void AddCustomCompare(string propertyName, Comparison<T> comparison) 
     { 
      _customComparisons.Add(propertyName, comparison); 
     } 

     /// <summary> 
     /// Apply sort. 
     /// </summary> 
     /// <param name="prop"></param> 
     /// <param name="direction"></param> 
     protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction) 
     { 
      Comparison<T> comparison; 
      if (!_customComparisons.TryGetValue(prop.Name, out comparison)) 
      { 
       // Check to see if the property type we are sorting by implements 
       // the IComparable interface. 
       Type interfaceType = prop.PropertyType.GetInterface("IComparable"); 
       if (interfaceType != null) 
       { 
        comparison = delegate(T t1, T t2) 
         { 
          IComparable val1 = (IComparable)prop.GetValue(t1); 
          IComparable val2 = (IComparable)prop.GetValue(t2); 
          return val1.CompareTo(val2); 
         }; 
       } 
       else 
       { 
        // Last option: convert to string and compare. 
        comparison = delegate(T t1, T t2) 
         { 
          string val1 = prop.GetValue(t1).ToString(); 
          string val2 = prop.GetValue(t2).ToString(); 
          return val1.CompareTo(val2); 
         }; 
       } 
      } 

      if (comparison != null) 
      { 
       // If so, set the SortPropertyValue and SortDirectionValue. 
       _sortPropertyValue = prop; 
       _sortDirectionValue = direction; 

       // Create sorted list. 
       List<T> _sortedList = new List<T>(this);     
       _sortedList.Sort(comparison); 

       // Reverse order if needed. 
       if (direction == ListSortDirection.Descending) 
       { 
        _sortedList.Reverse(); 
       } 

       // Update list. 
       int count = this.Count; 
       for (int i = 0; i < count; i++) 
       { 
        this[i] = _sortedList[i]; 
       } 

       // Raise the ListChanged event so bound controls refresh their 
       // values. 
       OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); 
      } 
     } 

     // Method below was in the original implementation from MS. Don't know what it's for. 
     // -- Martijn Boeker, Jan 21, 2010 

     //protected override void RemoveSortCore() 
     //{ 
     // //int position; 
     // //object temp; 
     // //// Ensure the list has been sorted. 
     // //if (unsortedItems != null) 
     // //{ 
     // // // Loop through the unsorted items and reorder the 
     // // // list per the unsorted list. 
     // // for (int i = 0; i < unsortedItems.Count;) 
     // // { 
     // //  position = this.Find(SortPropertyCore.Name, 
     // //   unsortedItems[i].GetType(). 
     // //   GetProperty(SortPropertyCore.Name). 
     // //   GetValue(unsortedItems[i], null)); 
     // //  if (position >= 0 && position != i) 
     // //  { 
     // //   temp = this[i]; 
     // //   this[i] = this[position]; 
     // //   this[position] = (T)temp; 
     // //   i++; 
     // //  } 
     // //  else if (position == i) 
     // //   i++; 
     // //  else 
     // //   // If an item in the unsorted list no longer exists, delete it. 
     // //   unsortedItems.RemoveAt(i); 
     // // } 
     // // OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); 
     // //} 
     //} 

     /// <summary> 
     /// Ability to search an item. 
     /// </summary> 
     protected override bool SupportsSearchingCore 
     { 
      get 
      { 
       return true; 
      } 
     } 

     /// <summary> 
     /// Finds an item in the list. 
     /// </summary> 
     /// <param name="prop"></param> 
     /// <param name="key"></param> 
     /// <returns></returns> 
     protected override int FindCore(PropertyDescriptor prop, object key) 
     { 
      // Implementation not changed from MS example code. 

      // Get the property info for the specified property. 
      PropertyInfo propInfo = typeof(T).GetProperty(prop.Name); 
      T item; 

      if (key != null) 
      { 
       // Loop through the the items to see if the key 
       // value matches the property value. 
       for (int i = 0; i < Count; ++i) 
       { 
        item = (T)Items[i]; 
        if (propInfo.GetValue(item, null).Equals(key)) 
         return i; 
       } 
      } 
      return -1; 
     } 

     /// <summary> 
     /// Finds an item in the list. 
     /// </summary> 
     /// <param name="prop"></param> 
     /// <param name="key"></param> 
     /// <returns></returns> 
     private int Find(string property, object key) 
     { 
      // Implementation not changed from MS example code. 

      // Check the properties for a property with the specified name. 
      PropertyDescriptorCollection properties = 
       TypeDescriptor.GetProperties(typeof(T)); 
      PropertyDescriptor prop = properties.Find(property, true); 

      // If there is not a match, return -1 otherwise pass search to 
      // FindCore method. 
      if (prop == null) 
       return -1; 
      else 
       return FindCore(prop, key); 
     } 

     #endregion 
    } 
} 
+0

Esta solución funciona bien, gracias – Drake

+0

1 para alguien poner finalmente en el espacios de nombres que incluyen. – SteveCav

0

Martijn código excelente pero sólo un detalle u necesidad de validar las células nulas o vacías :)

if (!_customComparisons.TryGetValue(prop.Name, out comparison)) 
{ 
    // Check to see if the property type we are sorting by implements 
    // the IComparable interface. 
    Type interfaceType = prop.PropertyType.GetInterface("IComparable"); 
    if (interfaceType != null) 
    { 
     comparison = delegate(T t1, T t2) 
      { 
       IComparable val1 = (IComparable)prop.GetValue(t1) ?? ""; 
       IComparable val2 = (IComparable)prop.GetValue(t2) ?? ""; 
       return val1.CompareTo(val2); 
      }; 
    } 
    else 
    { 
     // Last option: convert to string and compare. 
     comparison = delegate(T t1, T t2) 
      { 
       string val1 = (prop.GetValue(t1) ?? "").ToString(); 
       string val2 = (prop.GetValue(t2) ?? "").ToString(); 
       return val1.CompareTo(val2); 
      }; 
    } 
} 

Eso es todo suerte

1

me gustaría recomendar la sustitución:

jobs = new List<DisplayJob>(); 

con:

jobs = new SortableBindingList<DisplayJob>(); 

El código para SortableBindingList está aquí: Código http://www.timvw.be/presenting-the-sortablebindinglistt/

He usado esta basado en la producción sin ningún problema. La única limitación es que no es un tipo estable.

Si desea que la especie sea estable, reemplace:

itemsList.Sort(delegate(T t1, T t2) 
{ 
    object value1 = prop.GetValue(t1); 
    object value2 = prop.GetValue(t2); 

    return reverse * Comparer.Default.Compare(value1, value2); 
}); 

con un tipo de inserción:

int j; 
T index; 
for (int i = 0; i < itemsList.Count; i++) 
{ 
    index = itemsList[i]; 
    j = i; 

    while ((j > 0) && (reverse * Comparer.Default.Compare(prop.GetValue(itemsList[j - 1]), prop.GetValue(index)) > 0)) 
    { 
     itemsList[j] = itemsList[j - 1]; 
     j = j - 1; 
    } 

    itemsList[j] = index; 
} 
0

Did ha intentado establecer SortMemberPath para cada columna?

uxJobList.Columns[newColumn].SortMemberPath="Id";

y en lugar de simplemente utilizando Lista im ObservableCollection

Cuestiones relacionadas