DataView.Sort: más que solo asc / desc (necesita clasificación personalizada)

Tengo un informe creado a partir de un conjunto de datos. El conjunto de datos usa la propiedad Sort para ordenar los datos. Sé que puedo crear una expresión de ordenación como esta:

“campo desc, field2 asc”

Pero lo que necesito ahora es una forma de hacer un tipo personalizado. En SQL, puedo realizar una clasificación personalizada haciendo algo como esto:

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

Para básicamente redefinir mi clasificación (es decir, Some Value viene antes que Another Value).

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

Ok, acabo de actualizar esto rápidamente, y no hice todo el manejo de errores necesarios y la comprobación nula, pero debería darte una idea y debería ser suficiente para que comiences:

 public static class DataTableExtensions { public static DataView ApplySort(this DataTable table, Comparison comparison) { DataTable clone = table.Clone(); List rows = new List(); 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 Eleven

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 Eleven

13 Trece

12 Doce

14 Catorce

EDITAR: Lo cambió al 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.

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 solo estás usando el método de extensión en un DataView ).

Puede hacer esto en la DataTable original añadiéndole una DataColumn calculada (usando la propiedad Expression ) y luego ordenar su valor.

En tu 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 primero, AnotherValue segundo, y todo lo demás después de eso.

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í está la clase Versioning 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 agrega la columna a la tabla, en lugar de agregar Version como una cadena, la agrega como la clase Versioning:

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

Y luego ordena normalmente

 _view.Sort = "Version"; 

No lo creo. Sin embargo, podría cambiar su SQL para devolver una columna “CustomSort” que es el resultado de su statement de caso:

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

Puede usar una instrucción if o switch para obtener una funcionalidad similar a la sentencia case select:

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