DataView.Sort - больше, чем просто asc/desc (требуется индивидуальная сортировка)
У меня есть отчет, созданный из набора данных. В наборе данных используется свойство Sort для упорядочивания данных. Я знаю, что могу создать выражение вида следующим образом:
"поле desc, field2 asc"
Но теперь мне нужно сделать обычай. В SQL я могу выполнить пользовательский сортировку, выполнив что-то вроде этого:
order by
case when field = 'Some Value' then 0 end
case when field = 'Another Value' then 1 end
В основном переопределить мой тип (т.е. некоторое значение появляется перед другим значением).
Можно ли сделать что-то подобное, как выражение сортировки для DataView?
Ответы
Ответ 1
Хорошо, я просто быстро взломал это и не выполнял всю необходимую обработку ошибок и проверку нулей, но он должен дать вам представление и должен быть достаточно, чтобы вы начали:
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;
}
}
Использование:
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");
//Сортировка по StringValue:
DataView sorted = table.ApplySort((r, r2) =>
{
return ((string)r["StringValue"]).CompareTo(((string)r2["StringValue"]));
});
Результат:
11 Одиннадцать
14 Четырнадцать
10 Десять
13 Тринадцать
12 Двенадцать
//Сортировка по IntValue:
DataView sorted = table.ApplySort((r, r2) =>
{
return ((int)r["IntValue"]).CompareTo(((int)r2["IntValue"]));
});
Результат:
10 Десять
11 Одиннадцать
13 Тринадцать
12 Двенадцать
14 Четырнадцать
EDIT: изменил его на метод расширения.
Теперь в вашей Лямбде (или вы можете создать полномасштабный метод сравнения) вы можете выполнить любую логику пользовательской сортировки, которая вам нужна. Помните, что -1 меньше, 0 равно, а 1 больше.
Ответ 2
Мне нравится ответ BFree, хотя я буду беспокоиться о том, что мой код в конечном итоге будет обновлять клонированную таблицу, а не реальную. (Я не думал об этом достаточно, чтобы узнать, действительно ли это проблема, если вы используете только метод расширения в DataView
.)
Вы можете сделать это на исходном DataTable
, добавив к нему рассчитанный DataColumn
(используя свойство Expression
) и затем сортировка по его значению.
В вашем случае это будет что-то вроде:
DataColumn c = myTable.Columns.Add("Sort", typeof(int));
c.Expression = "iif(field='SomeValue', 0, iif(field='AnotherValue', 1, 2))";
который сначала сортирует SomeValue
, AnotherValue
второй и все остальное после этого.
Ответ 3
Я знаю, что этот пост немного старше, но я немного изменил его, выполнив IComparable. В этом примере я хотел сортировать по версии (которая находится в формате 0.0.0.0 в виде строки).
Вот класс Versioning, который реализует 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));
}
}
И когда вы добавляете столбец в таблицу, вместо добавления Версии в качестве строки вы добавляете ее как класс Версии:
_table.Columns.Add("Version", typeof(Versioning));
_view = new View(_table);
И затем выполните обычную сортировку:
_view.Sort = "Version";
Ответ 4
Я так не думаю. Однако вы можете изменить ваш SQL, чтобы вернуть столбец "CustomSort", который является результатом вашего оператора case:
select
(case when f = 'a' then 0 else 1 end) as CustomSort
from MyTable
Ответ 5
Вы можете использовать оператор if или switch, чтобы получить аналогичную функциональность для оператора case:
if (Something == "1")
MyView.Sort = "Field1 ASC";
else
MyView.Sort = "Field2 ASC";
ИЛИ
switch (MyProperty)
{
case 1:
MyView.Sort = "Field1 ASC";
break;
case 2:
MyView.Sort = "Field2 ASC";
break;
default:
MyView.Sort = "Field3 ASC";
break;
}