How can I convert a RenderFragment to a specific type?

59 Views Asked by At

I have a Blazor project with a custom component library based on Radzen components. One of the Radzen components I am using is RadzenDataGrid and I would like to reduce the amount of code that is currently required as I am using the component in different projects and the base is always the same. I created a custom component to replace RadzenDataGridColumn:

@* DataGridColumn.razor *@
@typeperam TItem;
@inherits RadzenDataGridColumn<TItem<;
<RadzenDataGridColumn TItme="TItem">
   <HeaderTemplate>
      <h1>@MyTitle</h1>
   </HeaderTemplate>
   @ChildContent
@code {
   [Parameter] public string MyTitle { get; set; }
   [Parameter] public RenderFragment ChildContent { get; set; }
}

@* Index.razor *@
...
<DataGridColumn MyTitle="Test" TItem="MyDataType" Property="Name">
   <Template Context="item">
      @item.Name
   </Template>
</DataGridColumn>

But I get the following error: Unrecognized child content inside component 'RadzenDataGridColumn'. The component 'RadzenDataGridColumn' accepts child content through the following top-level items: 'Columns', 'Template', 'EditTemplate', 'HeaderTemplate', ... However, I cannot put <Template> in DataGridColumn.razor because I need to access Context. Is there a way to cast the RenderFragment to type Template? Or to access Context inside the component? I'm still fairly new to Blazor, so please forgive me, if this is a trivial question.

2

There are 2 best solutions below

1
Henk Holterman On BEST ANSWER

Using inheritance

additonal answer using inheritance. It turns out to be very simple too, but it may depend on how the RadzenDataGridColumn handles null and defaults.

@* DataGridColumn.razor *@
@typeperam TItem
@inherits RadzenDataGridColumn<TItem>

@* no other markup here, just call the baseclass *@
@{ base.BuildRenderTree(__builder); }

@code {

 [Parameter] public string MyTitle { get; set; }

 protected override void OnInitialized()
 {
    // this can be made conditional with an if()
    HeaderTemplate = @<h1>@MyTitle</h1>;
 }

}

It's a pity but you will need base.BuildRenderTree(__builder);, which is using a little behind-the-scenes magic. You can avoid base.BuildRenderTree() by inheriting in a normal .cs file instead of .razor. But then you can't write RenderFragments like @<h1>@MyTitle</h1>.

All parameters are inherited so the pages could still supply a HeaderTemplate. Simply overwrite it or do some logic with is null.

3
Henk Holterman On

To begin with, your DataGridColumn both inherits from and embeds a RadzenDataGridColumn . That is too much, you only need one of those techniques.

@inherits RadzenDataGridColumn<TItem>    // 1
<RadzenDataGridColumn TItem="TItem"> ... // 2

So you have two options, inheritance or embedding. Both will work.

While inheritance might be a bit more efficient I think that embedding will be easier to start with. That should look like:

@* DataGridColumn.razor *@
@* Untested *@
@typeperam TItem;
<RadzenDataGridColumn TItem="TItem">
   <HeaderTemplate>
      <h1>@MyTitle</h1>
   </HeaderTemplate>
  <Template>
    @Template(context)
  </Template>
  <EditTemplate>
    @EditTemplate(context)
  </EditTemplate>
</RadzenDataGridColumn> 
@code {
   [Parameter] public string MyTitle { get; set; }
//   [Parameter] public RenderFragment ChildContent { get; set; }

  [Parameter] public RenderFragment<TItem> Template { get; set; }
  [Parameter] public RenderFragment<TItem> EditTemplate { get; set; }

}

What you are doing here is to intercept the HeaderTemplate and pass the other Renderfragments through.