Search code examples
c#blazormudblazor

MudBlazor with Dynamic Columns


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?


Solution

  • 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.