2009-02-24 7 views
12

Tengo un informe creado a partir de un conjunto de datos. El conjunto de datos usa la propiedad Sort para ordenar los datos. Yo sé que puedo crear una expresión de ordenación de esta manera:DataView.Sort: algo más que asc/desc (necesita clasificación personalizada)

"desc campo, asc campo2"

Pero lo que necesito ahora es una manera de hacer una especie de encargo. En SQL, puedo realizar una ordenación personalizada al hacer algo como esto:

order by 
    case when field = 'Some Value' then 0 end 
    case when field = 'Another Value' then 1 end 

Para básicamente volver a definir mi tipo (es decir, un valor viene antes de otro valor).

¿Es posible hacer algo similar como una expresión de ordenación frente a un DataView?

Respuesta

15

Ok, acabo de azotar esto muy rápido, y no hacer todo el manejo de errores y comprobación neccessary nula, pero debe darle una idea y debe ser enou gh para empezar:

public static class DataTableExtensions 
{ 
    public static DataView ApplySort(this DataTable table, Comparison<DataRow> comparison) 
    { 

     DataTable clone = table.Clone(); 
     List<DataRow> rows = new List<DataRow>(); 
     foreach (DataRow row in table.Rows) 
     { 
      rows.Add(row);  
     } 

     rows.Sort(comparison); 

     foreach (DataRow row in rows) 
     { 
      clone.Rows.Add(row.ItemArray); 
     } 

     return clone.DefaultView; 
    } 


} 

Uso:

DataTable table = new DataTable(); 
    table.Columns.Add("IntValue", typeof(int)); 
    table.Columns.Add("StringValue"); 

    table.Rows.Add(11, "Eleven"); 
    table.Rows.Add(14, "Fourteen"); 
    table.Rows.Add(10, "Ten"); 
    table.Rows.Add(12, "Twelve"); 
    table.Rows.Add(13, "Thirteen"); 

// Ordenar por StringValue:

DataView sorted = table.ApplySort((r, r2) => 
     { 
      return ((string)r["StringValue"]).CompareTo(((string)r2["StringValue"])); 
     }); 

Resultado:

11 Once

14 Catorce

10 Diez

13 Trece

12 Doce

// Ordenar por intValue:

DataView sorted = table.ApplySort((r, r2) => 
      { 
       return ((int)r["IntValue"]).CompareTo(((int)r2["IntValue"])); 
      }); 

Resultado:

10 Diez

11 Once

13 Trece

12 Doce

14 Catorce

EDIT: cambió a método de extensión.

Ahora en su Lambda, (o puede crear un método de comparación completo) puede hacer cualquier tipo de lógica de clasificación personalizada que necesite. Recuerde, -1 es menor que, 0 es igual a, y 1 es mayor que.

+5

Te debo una cerveza. – Orestes

+0

Gran respuesta. Solo necesité un poco de ajuste con respecto a la clasificación en dos direcciones y vinculando la vista de nuevo a la cuadrícula. – tys

1

No lo creo. sin embargo, puede cambiar que SQL para devolver una columna "CustomSort" que es el resultado de su declaración de caso:

select 
    (case when f = 'a' then 0 else 1 end) as CustomSort 
from MyTable 
+0

Ésta es una solución posible; con suerte, uno que no tengo que usar, porque significaría editar el SQL de más de 20 informes. – bugfixr

1

Se podría utilizar un si o un interruptor de declaración para obtener una funcionalidad similar a la declaración de caso selecto:

  if (Something == "1") 
       MyView.Sort = "Field1 ASC"; 
      else 
       MyView.Sort = "Field2 ASC"; 

O

  switch (MyProperty) 
      { 
       case 1: 
        MyView.Sort = "Field1 ASC"; 
        break; 
       case 2: 
        MyView.Sort = "Field2 ASC"; 
        break; 
       default: 
        MyView.Sort = "Field3 ASC"; 
        break; 
      } 
15

Me gusta la respuesta de BFree, aunque me preocuparía el riesgo de que mi código termine por actualizar la tabla clonada en lugar de la real. (No he pensado lo suficiente como para saber si eso es realmente un problema si sólo está utilizando el método de extensión en un DataView.)

Usted puede hacer esto en el original DataTable mediante la adición de una calculada DataColumn a ella (usando la propiedad Expression) y luego ordenando su valor.

En su caso sería algo así como:

DataColumn c = myTable.Columns.Add("Sort", typeof(int)); 
c.Expression = "iif(field='SomeValue', 0, iif(field='AnotherValue', 1, 2))"; 

que ordena SomeValue primera, AnotherValue segundos, y todo lo demás después de eso.

+0

Hola, necesito este tipo de solución. ¿Entonces todo lo que tengo que hacer es usar estas líneas de código solo? Corrígeme si estoy equivocado. Además, ¿qué sucede si hay más de 2 columnas que quiero ordenar? Como por ejemplo, una columna tiene estos valores por fila "A", "B", "C", "D". Quiero que la orden sea B, A, D, C. ¿Cómo hago esto usando su aplicación? lo siento, soy un poco nuevo con .net. Usando VB en este – Wax

+0

+1 Esta es una solución ideal cuando la clonación de la tabla de datos no es una opción, ni es una técnica similar como la sustitución de una vista de datos diferente. Casi todas las otras soluciones que he encontrado repartidas entre StackOverflow y el resto de Internet implican algún tipo de duplicación de los datos originales, lo que puede ser costoso para tablas grandes y puede llevar a problemas de sincronización en inserciones/actualizaciones/eliminaciones, etc. Ojalá pudiera +10 esto. ;) – SQLServerSteve

4

Sé que esta publicación es un poco más antigua, pero fui algo diferente al implementar IComparable. En este ejemplo, quería ordenar por versión (que está en el formato 0.0.0.0 como una cadena).

Aquí es la clase de versiones que implementa IComparable:

public class Versioning : IComparable { 
    string _version; 

    int _major; 
    public int Major { 
     get { return (_major); } 
     set { _major = value; } 
    } 

    int _minor; 
    public int Minor { 
     get { return (_minor); } 
     set { _minor = value; } 
    } 

    int _beta; 
    public int Beta { 
     get { return (_beta); } 
     set { _beta = value; } 
    } 

    int _alpha; 
    public int Alpha { 
     get { return (_alpha); } 
     set { _alpha = value; } 
    } 

    public Versioning(string version) { 
     _version = version; 

     var splitVersion = SplitVersion(); 
     if (splitVersion.Length < 4) { 
      Major = Minor = Beta = Alpha = 0; 
     } 

     if (!int.TryParse(splitVersion[0], out _major)) _major = 0; 
     if (!int.TryParse(splitVersion[1], out _minor)) _minor = 0; 
     if (!int.TryParse(splitVersion[2], out _beta)) _beta = 0; 
     if (!int.TryParse(splitVersion[3], out _alpha)) _alpha = 0; 
    } 

    string[] SplitVersion() { 
     return (_version.Split('.')); 
    } 

    int GetCompareTo(Versioning versioning) { 
     var greater = -1; 
     var equal = 0; 
     var less = 1; 

     if (Major > versioning.Major) return (greater); 
     if (Major < versioning.Major) return (less); 
     if (Minor > versioning.Minor) return (greater); 
     if (Minor < versioning.Minor) return (less); 
     if (Beta > versioning.Beta) return (greater); 
     if (Beta < versioning.Beta) return (less); 
     if (Alpha > versioning.Alpha) return (greater); 
     if (Alpha < versioning.Alpha) return (less); 

     return (equal); 
    } 

    public int CompareTo(Versioning versioning) { 
     return (GetCompareTo(versioning)); 
    } 

    public override string ToString() { 
     return (_version); 
    } 

    public int CompareTo(object obj) { 
     if (obj == null) return (1); 
     return (GetCompareTo((Versioning)obj)); 
    } 
} 

Y cuando se agrega la columna a la tabla, en lugar de añadir la versión como una cadena, se agrega como la clase de versiones:

_table.Columns.Add("Version", typeof(Versioning)); 
_view = new View(_table); 

Y a continuación, ordenar normalmente:

_view.Sort = "Version"; 
Cuestiones relacionadas