Skip to content

Commit c98e2d6

Browse files
kb(common): render fragment null exception
1 parent e622465 commit c98e2d6

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
---
2+
title: RenderFragment Parameter Causes Exception
3+
description: Having a RenderFragment Parameter used as a property causes Delegate to an instance method cannot have null 'this'
4+
type: troubleshooting
5+
page_title: RenderFragment Parameter Causes Exception
6+
slug: common-kb-render-fragment-parameter-null
7+
position:
8+
tags:
9+
ticketid: 1449470
10+
res_type: kb
11+
---
12+
13+
14+
15+
## Description
16+
17+
When I use a `RenderFragment` that comes as a parameter (for example, because I am wrapping a component that already uses a `RenderFragment` in another component), I get an exception.
18+
19+
20+
## Error Message
21+
22+
> Error: ArgumentException: Delegate to an instance method cannot have null 'this'.
23+
>
24+
> at System.MulticastDelegate.ThrowNullThisInDelegateToInstance()
25+
>
26+
> at System.MulticastDelegate.CtorClosed(Object target, IntPtr methodPtr)
27+
>
28+
> at YourCustomComponentNameHere`2.BuildRenderTree(RenderTreeBuilder __builder)
29+
30+
31+
## Cause\Possible Cause(s)
32+
33+
`RenderFragment` parameters are `null` by default and there is no really suitable place to make a check when passing the paramter as a property. You can read more here: [https://github.com/dotnet/aspnetcore/issues/10270](https://github.com/dotnet/aspnetcore/issues/10270)
34+
35+
So, for example, with a Telerik component such as the ComboBox, such templates (fragments) will throw exceptions if you pass them as a property.
36+
37+
38+
## Steps to Reproduce
39+
40+
The following sample used as a component will cause exceptions:
41+
42+
>caption RenderFragment elements used as properties cause exceptions
43+
44+
````CSHTML
45+
typeparam TItem
46+
@typeparam TValue
47+
48+
<TelerikComboBox Data="@Data" Value="@Value" ValueChanged="@ValueChanged" ValueExpression="@ValueExpression" OnChange="@OnChange"
49+
TItem="TItem" TValue="TValue" ValueField="@ValueField" TextField="@TextField"
50+
51+
52+
HeaderTemplate="@HeaderTemplate" FooterTemplate="@FooterTemplate" ItemTemplate="@ItemTemplate">
53+
</TelerikComboBox>
54+
55+
@code {
56+
[Parameter] public IEnumerable<TItem> Data { get; set; }
57+
[Parameter] public TValue Value { get; set; }
58+
[Parameter] public string ValueField { get; set; }
59+
[Parameter] public string TextField { get; set; }
60+
[Parameter] public EventCallback<TValue> ValueChanged { get; set; }
61+
[Parameter] public System.Linq.Expressions.Expression<System.Func<TValue>> ValueExpression { get; set; }
62+
[Parameter] public EventCallback<Object> OnChange { get; set; }
63+
[Parameter] public RenderFragment FooterTemplate { get; set; }
64+
[Parameter] public RenderFragment HeaderTemplate { get; set; }
65+
[Parameter] public RenderFragment<TItem> ItemTemplate { get; set; }
66+
}
67+
````
68+
69+
>caption Simple usage of the problematic component that causes an error
70+
71+
````CSHTML
72+
<CustomCombo Data="@myDdlData" TItem="@MyDdlModel" TValue="int"
73+
TextField="MyTextField" ValueField="MyValueField" @bind-Value="@selectedValue">
74+
</CustomCombo>
75+
76+
@* If you define the three templates, you will not get exceptions, however *@
77+
78+
@code {
79+
public class MyDdlModel
80+
{
81+
public int MyValueField { get; set; }
82+
public string MyTextField { get; set; }
83+
}
84+
85+
IEnumerable<MyDdlModel> myDdlData = Enumerable.Range(1, 20).Select(x => new MyDdlModel { MyTextField = "item " + x, MyValueField = x });
86+
87+
int selectedValue { get; set; } = 3;
88+
}
89+
````
90+
91+
92+
## Solution
93+
94+
The solution is to implement checks in the custom component so that you can render only when something is actually present, and to render it in a tag. Here is an example:
95+
96+
>caption Custom Component wrapping a component that uses a RenderFragment
97+
98+
````CSHTML
99+
@typeparam TItem
100+
@typeparam TValue
101+
102+
@using System.Reflection
103+
104+
<TelerikComboBox Data="@Data" Value="@Value" ValueChanged="@ValueChanged" ValueExpression="@ValueExpression" OnChange="@OnChange"
105+
TItem="TItem" TValue="TValue" ValueField="@ValueField" TextField="@TextField">
106+
<HeaderTemplate>
107+
@HeaderTemplate
108+
</HeaderTemplate>
109+
<ItemTemplate>
110+
@if (ItemTemplate != null)
111+
{
112+
@ItemTemplate((TItem)context)
113+
}
114+
else
115+
{
116+
object itm = context;
117+
string toRender = "";
118+
PropertyInfo propertyInfo = itm.GetType().GetProperty(TextField);
119+
120+
if (propertyInfo != null)
121+
{
122+
toRender = propertyInfo.GetValue(itm).ToString();
123+
}
124+
@toRender
125+
}
126+
</ItemTemplate>
127+
<FooterTemplate>
128+
@FooterTemplate
129+
</FooterTemplate>
130+
</TelerikComboBox>
131+
132+
@code {
133+
[Parameter] public IEnumerable<TItem> Data { get; set; }
134+
[Parameter] public TValue Value { get; set; }
135+
[Parameter] public string ValueField { get; set; }
136+
[Parameter] public string TextField { get; set; }
137+
[Parameter] public EventCallback<TValue> ValueChanged { get; set; }
138+
[Parameter] public System.Linq.Expressions.Expression<System.Func<TValue>> ValueExpression { get; set; }
139+
[Parameter] public EventCallback<Object> OnChange { get; set; }
140+
[Parameter] public RenderFragment FooterTemplate { get; set; }
141+
[Parameter] public RenderFragment HeaderTemplate { get; set; }
142+
[Parameter] public RenderFragment<TItem> ItemTemplate { get; set; }
143+
}
144+
````
145+
146+
>caption How to consume that component - like usual
147+
148+
````CSHTML
149+
@selectedValue
150+
151+
<hr />
152+
153+
<CustomCombo Data="@myDdlData" TItem="@MyDdlModel" TValue="int"
154+
TextField="MyTextField" ValueField="MyValueField" @bind-Value="@selectedValue">
155+
<HeaderTemplate>
156+
<h4>header stuff</h4>
157+
</HeaderTemplate>
158+
<ItemTemplate>
159+
<strong>test</strong>&nbsp;@( (context as MyDdlModel).MyTextField )
160+
</ItemTemplate>
161+
<FooterTemplate>
162+
The footer
163+
</FooterTemplate>
164+
</CustomCombo>
165+
166+
<hr />
167+
168+
@* This also works now if you don not pass any templates, which would throw exceptions if the render fragments were properties *@
169+
170+
<CustomCombo Data="@myDdlData" TItem="@MyDdlModel" TValue="int"
171+
TextField="MyTextField" ValueField="MyValueField" @bind-Value="@selectedValue">
172+
</CustomCombo>
173+
174+
@code {
175+
public class MyDdlModel
176+
{
177+
public int MyValueField { get; set; }
178+
public string MyTextField { get; set; }
179+
}
180+
181+
IEnumerable<MyDdlModel> myDdlData = Enumerable.Range(1, 20).Select(x => new MyDdlModel { MyTextField = "item " + x, MyValueField = x });
182+
183+
int selectedValue { get; set; } = 3;
184+
}
185+
````
186+

0 commit comments

Comments
 (0)