This article shows code how you can create a DataTable from a collection of T (IEnumerable<T>) and defining explicitly the column order.
An extension method for this looks like the following:
publicstaticclassDataTableExtensions
{
publicstatic DataTable CreateOrderedDataTable<T>(this IEnumerable<T> data)
{
var dataTable = new DataTable();
var orderedProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
.OrderBy(prop => GetColumnOrder(prop)).ToList();
foreach (var prop in orderedProps){
dataTable.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
if (data != null)
{
dataTable.BeginLoadData();
var enumerator = data.GetEnumerator();
while (enumerator.MoveNext()){
var item = enumerator.Current;
var rowValues = new List<object>();
foreach (var prop in orderedProps){
rowValues.Add(prop.GetValue(item, null));
}
dataTable.Rows.Add(rowValues.ToArray());
}
dataTable.AcceptChanges();
}
return dataTable;
}
staticintGetColumnOrder(PropertyInfo prop)
{
var displayAttribute = prop.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault() as DisplayAttribute;
int orderKey = displayAttribute?.Order ?? prop.MetadataToken;
return orderKey;
}
}
We order first by DisplayAttribute and the Order value, and fallback to property's MetadataToken. This is an integer value that also returns the order the property was declared,
in case you want to order just by the way properties are defined. We get the enumerator here and fetch the row one by one. We could use a simple foreach loop here too. Note the use of BeginLoadData
and AcceptChanges.
Consider the two classes next. One class does not set any explicit order, the other class uses the Display attribute's Order value to define a custom order of columns for the DataTable.
publicclassCar
{
publicint Id { get; set; }
publicstring Make { get; set; }
publicstring Model { get; set; }
publicstring Color { get; set; }
}
publicclassCarV2
{
[Display(Order = 4)]
publicint Id { get; set; }
[Display(Order = 3)]
publicstring Make { get; set; }
[Display(Order = 2)]
publicstring Model { get; set; }
[Display(Order = 14)]
publicbool IsElectric { get; set; }
[Display(Order = -188865)]
publicstring Color { get; set; }
}
Next, the following little program in Linqpad tests this extension method and displays the datatables resulting with column ordering set.
voidMain()
{
var cars = new List<Car>{
new Car { Id = 1, Make = "Audi", Model = "A5", Color = "Blue" },
new Car { Id = 2, Make = "Volvo", Model = "XC60", Color = "Silver" },
new Car { Id = 3, Make = "Golf", Model = "GTI", Color = "White" },
new Car { Id = 4, Make = "Audi", Model = "A5", Color = "Blue" },
};
var dataTable = cars.CreateOrderedDataTable();
dataTable.Dump("Cars datatable, data type is: Car");
var carV2s = new List<CarV2>{
new CarV2 { Id = 1, Make = "Audi", Model = "A5", Color = "Blue" },
new CarV2 { Id = 2, Make = "Volvo", Model = "XC60", Color = "Silver" },
new CarV2 { Id = 3, Make = "Golf", Model = "GTI", Color = "White" },
new CarV2 { Id = 4, Make = "Audi", Model = "A5", Color = "Blue" },
};
var dataTableV2 = carV2s.CreateOrderedDataTable();
dataTableV2.Dump("Carsv2 datatable, datatype is CarV2");
}