2009-02-24 9 views
12

J'ai un rapport en cours de construction à partir d'un ensemble de données. L'ensemble de données utilise la propriété Sort pour ordonner les données. Je sais que je peux créer une expression de tri comme celui-ci:DataView.Sort - plus que simplement asc/desc (besoin de tri personnalisé)

« champ desc, field2 asc »

Mais ce que je dois maintenant est un moyen de faire une sorte de coutume. Dans SQL, je peux effectuer une sorte personnalisée en faisant quelque chose comme ceci:

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

Pour redéfinir fondamentalement mon genre (i.e., une certaine valeur vient avant une autre valeur).

Est-il possible de faire quelque chose de similaire à une expression de tri par rapport à un DataView?

Répondre

15

Ok, je viens de le fouet jusqu'à très rapide, et n'a pas fait tout le traitement et le contrôle nul erreur neccessary, mais il devrait vous donner une idée et devrait être enou gh pour vous aider à démarrer:

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


} 

Utilisation:

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

// Trier par StringValue:

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

Résultat:

11 Onze

14 Fourteen

10 Ten

13 Treize

12 Douze

// Trier par IntValue:

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

Résultat:

10 Ten

11 Onze

13 Treize

12 Douze

14 Quatorze

EDIT: changé en mode d'extension.Maintenant, dans votre Lambda, (ou vous pouvez créer une méthode de Comparaison complète), vous pouvez faire toute sorte de logique de tri personnalisée dont vous avez besoin. Rappelez-vous, -1 est inférieur à, 0 est égal à, et 1 est supérieur à.

+5

Je vous dois une bière. – Orestes

+0

Bonne réponse. Juste besoin d'un peu de peaufinage en ce qui concerne le tri à deux voies et la liaison de la vue dans la grille. – tys

1

Je ne pense pas. Vous pouvez cependant vous changer SQL pour retourner une colonne « CustomSort » qui est le résultat de votre déclaration de cas:

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

Ceci est une solution possible; j'espère que je ne dois pas utiliser parce que cela signifierait éditer le SQL de plus de 20 rapports. – bugfixr

1

Vous pouvez utiliser un si ou une instruction switch pour obtenir une fonctionnalité similaire à l'instruction select de cas:

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

OU

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

J'aime la réponse de BFree, bien que je m'inquiète du risque que mon code finisse par mettre à jour la table clonée plutôt que la vraie. (Je ne l'ai pas réfléchi assez pour savoir si c'est en fait un problème si vous êtes seulement en utilisant la méthode d'extension dans un DataView.)

Vous pouvez le faire sur la DataTable originale en ajoutant un calculé DataColumn à elle (en utilisant la propriété Expression), puis en triant sa valeur.

Dans votre cas, il serait quelque chose comme:

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

qui trie SomeValue premier, AnotherValue secondes, et tout le reste après.

+0

Bonjour, j'ai besoin de ce genre de solution. Donc, tout ce que j'ai à faire est d'utiliser ces lignes de code seul? Corrige moi si je me trompe. Aussi, que faire s'il y a plus de 2 colonnes que je veux trier? Comme par exemple, une colonne a ces valeurs par ligne "A", "B", "C", "D". Je veux que la commande soit B, A, D, C. Comment est-ce que je fais cela en utilisant votre application? désolé, je suis un peu nouveau avec .net. Utilisation de VB sur celui-ci – Wax

+0

+1 Ceci est une solution idéale lorsque le clonage de la datatable n'est pas une option, pas plus qu'une technique similaire comme la substitution d'une vue de données différente. Presque toutes les autres solutions que j'ai trouvées dispersées dans StackOverflow et le reste d'Internet impliquent une sorte de duplication des données d'origine, ce qui peut être coûteux pour les grandes tables et peut entraîner des problèmes de synchronisation sur les insertions/mises à jour/suppressions etc. Je voudrais pouvoir +10 cela. ;) – SQLServerSteve

4

Je sais que cet article est un peu plus ancien, mais je suis allé à ce sujet légèrement différent en implémentant IComparable. Dans cet exemple, je voulais trier par version (qui est au format 0.0.0.0 sous forme de chaîne).

Voici la classe Versioning qui implémente 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)); 
    } 
} 

Et lorsque vous ajoutez la colonne à la table, au lieu d'ajouter la version sous forme de chaîne, vous ajoutez comme la classe Versioning:

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

Et puis trier normalement:

_view.Sort = "Version"; 
Questions connexes