Skip to content

Commit fc1f556

Browse files
authored
GridView should support DataTable (#187)
* GridView supports DataTable
1 parent 840331c commit fc1f556

File tree

5 files changed

+170
-62
lines changed

5 files changed

+170
-62
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
@using System.Data
2+
@using BlazorWebFormsComponents.Extensions
3+
@inherits TestComponentBase
4+
5+
<Fixture Test="DataTableAsDataSourceWithAutoGenerateColumns">
6+
<ComponentUnderTest>
7+
<GridView DataSource="dataTable.AsDynamicEnumerable()" AutogenerateColumns="true">
8+
</GridView>
9+
</ComponentUnderTest>
10+
</Fixture>
11+
12+
<Fixture Test="DataTableAsDataSourceWithoutAutoGenerateColumns">
13+
<ComponentUnderTest>
14+
<GridView ItemType="dynamic" DataSource="dataTable.AsDynamicEnumerable()" AutogenerateColumns="false">
15+
<Columns>
16+
<BoundField ItemType="dynamic" DataField="Name" HeaderText="Item Name" />
17+
<BoundField ItemType="dynamic" DataField="Quantity" HeaderText="Item Quantity" />
18+
</Columns>
19+
</GridView>
20+
</ComponentUnderTest>
21+
</Fixture>
22+
23+
<Fixture Test="DataTableAsDataSource">
24+
<ComponentUnderTest>
25+
<GridView DataSource="dataTable.AsEnumerable()" AutogenerateColumns="true">
26+
</GridView>
27+
</ComponentUnderTest>
28+
</Fixture>
29+
30+
@code {
31+
DataTable dataTable = new DataTable();
32+
33+
void DataTableAsDataSourceWithAutoGenerateColumns(Fixture fixture)
34+
{
35+
dataTable = GetDataTable();
36+
37+
var cut = fixture.GetComponentUnderTest();
38+
var tableHeaders = cut.FindAll("th");
39+
tableHeaders[0].TextContent.ShouldBe("Id");
40+
tableHeaders[1].TextContent.ShouldBe("Name");
41+
tableHeaders[2].TextContent.ShouldBe("Quantity");
42+
tableHeaders.Count.ShouldBe(3, "Did not render 3 TH elements");
43+
}
44+
45+
void DataTableAsDataSourceWithoutAutoGenerateColumns(Fixture fixture)
46+
{
47+
dataTable = GetDataTable();
48+
49+
var cut = fixture.GetComponentUnderTest();
50+
var tableHeaders = cut.FindAll("th");
51+
tableHeaders[0].TextContent.ShouldBe("Item Name");
52+
tableHeaders[1].TextContent.ShouldBe("Item Quantity");
53+
tableHeaders.Count.ShouldBe(2, "Did not render 2 TH elements");
54+
}
55+
56+
void DataTableAsDataSource(Fixture fixture)
57+
{
58+
dataTable = GetDataTable();
59+
60+
var cut = fixture.GetComponentUnderTest();
61+
var tableHeaders = cut.FindAll("th");
62+
tableHeaders[0].TextContent.ShouldBe(nameof(DataRow.RowError));
63+
tableHeaders[1].TextContent.ShouldBe(nameof(DataRow.RowState));
64+
tableHeaders[2].TextContent.ShouldBe(nameof(DataRow.Table));
65+
tableHeaders[3].TextContent.ShouldBe(nameof(DataRow.ItemArray));
66+
tableHeaders[4].TextContent.ShouldBe(nameof(DataRow.HasErrors));
67+
tableHeaders.Count.ShouldBe(5, "Did not render 5 TH elements");
68+
}
69+
70+
DataTable GetDataTable()
71+
{
72+
dataTable.Columns.AddRange(new DataColumn[3]
73+
{
74+
new DataColumn("Id", typeof(int)),
75+
new DataColumn("Name", typeof(string)),
76+
new DataColumn("Quantity",typeof(string))
77+
});
78+
79+
dataTable.Rows.Add(1, "Flour", "500 Kg");
80+
dataTable.Rows.Add(2, "Tea", "20 Kg");
81+
dataTable.Rows.Add(3, "Rice", "1000 Kg");
82+
83+
return dataTable;
84+
}
85+
}

src/BlazorWebFormsComponents/Data/DataRow.cs

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/BlazorWebFormsComponents/Data/DataTable.cs

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data;
4+
using System.Linq;
5+
using System.Reflection;
6+
using System.Reflection.Emit;
7+
8+
namespace BlazorWebFormsComponents.Extensions
9+
{
10+
public static class DataTableExtensions
11+
{
12+
public static IEnumerable<dynamic> AsDynamicEnumerable(this DataTable table)
13+
{
14+
if (table == null)
15+
{
16+
yield break;
17+
}
18+
19+
var columns = table.Columns.Cast<DataColumn>();
20+
var columnNames = columns.Select(column => column.ColumnName);
21+
var rows = table.AsEnumerable()
22+
.Select(dataRow => columns.Select(column => new { Column = column.ColumnName, Value = dataRow[column] })
23+
.ToDictionary(data => data.Column, data => data.Value))
24+
.ToList();
25+
var objectType = GetObjectType(columnNames);
26+
foreach (var row in rows)
27+
{
28+
var obj = Activator.CreateInstance(objectType);
29+
var properties = obj.GetType().GetProperties();
30+
foreach (var rowData in row)
31+
{
32+
foreach (var property in properties)
33+
{
34+
if (property.Name == rowData.Key)
35+
{
36+
property.SetValue(obj, rowData.Value.ToString(), null);
37+
break;
38+
}
39+
}
40+
}
41+
42+
yield return obj;
43+
}
44+
}
45+
46+
private static Type GetObjectType(IEnumerable<string> propertiesNames)
47+
{
48+
var assemblyName = new AssemblyName("TempAssembly");
49+
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
50+
var moduleBuilder = assemblyBuilder.DefineDynamicModule("TempModule");
51+
var typeBuilder = moduleBuilder.DefineType("GridRowCellCollection", TypeAttributes.Public);
52+
foreach (var propertyName in propertiesNames)
53+
{
54+
var fieldBuilder = typeBuilder.DefineField(propertyName, typeof(string), FieldAttributes.Public);
55+
var propertyBuilder = typeBuilder.DefineProperty(propertyName, System.Reflection.PropertyAttributes.None, typeof(string), new Type[] { typeof(string) });
56+
57+
var getPropertyMethodBuilder = typeBuilder.DefineMethod("get_value", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(string), Type.EmptyTypes);
58+
var getILGenerator = getPropertyMethodBuilder.GetILGenerator();
59+
getILGenerator.Emit(OpCodes.Ldarg_0);
60+
getILGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
61+
getILGenerator.Emit(OpCodes.Ret);
62+
propertyBuilder.SetGetMethod(getPropertyMethodBuilder);
63+
64+
var setPropertyMethodBuilder = typeBuilder.DefineMethod("set_value", MethodAttributes.Public | MethodAttributes.HideBySig, null, new Type[] { typeof(string) });
65+
var setILGenerator = setPropertyMethodBuilder.GetILGenerator();
66+
setILGenerator.Emit(OpCodes.Ldarg_0);
67+
setILGenerator.Emit(OpCodes.Ldarg_1);
68+
setILGenerator.Emit(OpCodes.Stfld, fieldBuilder);
69+
setILGenerator.Emit(OpCodes.Ret);
70+
propertyBuilder.SetSetMethod(setPropertyMethodBuilder);
71+
}
72+
73+
return typeBuilder.CreateType();
74+
}
75+
}
76+
}

src/BlazorWebFormsComponents/GridViewColumnGenerator.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace BlazorWebFormsComponents
88
/// </summary>
99
public static class GridViewColumnGenerator
1010
{
11+
private const string IndexerPropertyName = "Item";
12+
1113
/// <summary>
1214
/// Generate columns for a given GridView based on the properties of given Type
1315
/// </summary>
@@ -16,8 +18,13 @@ public static class GridViewColumnGenerator
1618
public static void GenerateColumns<ItemType>(GridView<ItemType> gridView)
1719
{
1820
var type = typeof(ItemType);
19-
var propertiesInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).OrderBy(x => x.MetadataToken);
20-
foreach (var propertyInfo in propertiesInfo)
21+
var propertiesInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
22+
if (propertiesInfo.Count() == 0)
23+
{
24+
propertiesInfo = gridView.DataSource.First()?.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public) ?? Enumerable.Empty<PropertyInfo>().ToArray();
25+
}
26+
27+
foreach (var propertyInfo in propertiesInfo.Where(p => p.Name != IndexerPropertyName).OrderBy(x => x.MetadataToken))
2128
{
2229
var newColumn = new BoundField<ItemType> {
2330
DataField = propertyInfo.Name,

0 commit comments

Comments
 (0)