I'd like to create a way to create dynamic columns using PropertyColumn in MudBlazor Datagrid.
<MudDataGrid @ref=_grid Items="GetItems(Model.ModelClass.Object, Items)">
<Columns>
@foreach(ListViewColumn col in Listview.Columns.OrderBy(x => x.Index))
{
<PropertyColumn Property="@MakeExpression(col, Model.ModelClass.Object, PropertyMatch(col).PropertyType)" Title="@col.Caption" />
}
</Columns>
</MudDataGrid>
@code{
Expression<Func<T, P>> MakeExpression<T, P>(ListViewColumn col, T obj, P item) where T : Type
{
var text = $"x => x.{col.PropertyName}";
return System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda<T, P>(new System.Linq.Dynamic.Core.ParsingConfig(), true, text);
}
}
However, it is still giving me an exception: No property or field 'String1' exists in type 'Type''
String1 is the propertyName.. Model.ModelClass.Object is the type to reflect the T of the MudDataGrid
How do I fix my Expression<Func<T,P>> method?
Here is how i made it work.
Firstly, create a GenericComponent call DataGrid, which contains the DataGrid:
@typeparam T
<MudDataGrid T="T" Items="@Items">
<Columns>
<SelectColumn T="T" />
@foreach (var PInfo in typeof(T).GetProperties())
{
if (PInfo.PropertyType == typeof(string))
{
<PropertyColumn Property="@(GetStringPropertyLambdaExpression(PInfo))" />
}
}
</Columns>
</MudDataGrid>
code{
[Parameter]
public string Title { get; set; }
[Parameter]
public IEnumerable<T> Items { get; set; }
private Expression<Func<T, string>> GetStringPropertyLambdaExpression(System.Reflection.PropertyInfo propertyInfo)
{
// Define a parameter for the lambda expression
ParameterExpression param = Expression.Parameter(typeof(T), "x");
// Create an expression to access the property
Expression propertyAccess = Expression.Property(param, propertyInfo);
// Create a lambda expression that takes an AType parameter and returns its Name property
Expression<Func<T, string>> lambdaExpression = Expression.Lambda<Func<T, string>>(propertyAccess, param);
return lambdaExpression;
}
}
Then i just use it in my page like that: dataname is the name of my datasource. On parameter set. I build the datasource depend on its name. It can be list of person, contact list, product list...
@page "/infolist/{*dataname}"
<MudText Typo="Typo.caption" >@dataname</MudText>
@InfoGrid
Code block of the page, where my datagrid is added after parameters are set
protected override async Task OnParametersSetAsync()
{
try
{
await base.OnParametersSetAsync();
if (DataNameChanged)
{
await vm.LoadAsync();
InfoGrid = AddGridView();
StateHasChanged();
}
}
catch (Exception ex)
{
await alert.ShowErrorAsync(ex);
}
}
private RenderFragment InfoGrid { get; set; }
private RenderFragment AddGridView() => __builder =>
{
var ItemType = ViewModel.RowDataType;
var sample = Activator.CreateInstance(ItemType);
var genericType = typeof(GridView<>);
var theGridType = genericType.MakeGenericType(ItemType);
__builder.OpenComponent(11, theGridType);
__builder.AddAttribute(23, "Items", ViewModel.Items);
__builder.CloseComponent();
};
The Property Attribute of Mud PropertyColumn is Expression<Func<T,TProperty>>.
I don't know how or where to define TProperty, so I've fixed it as string, decimal, datetime... It's ugly, but it is the best solution I can come up with for now. I hope someone can enlighten me with a better solution.