Blazor Cascading Parameters don’t “just work” with lambdas or method callback

TL;DR: Blazor Cascading Parameters are matched by Type not necessarily by Name. The simplest solution is to declare the Parameter type as Delegate. If you need to distinguish more than one delegate cascading parameter, then declare a named delegate type (that's a one-liner in C#) and use that as the parameter type.

The problem

You want to pass a callback as a cascading parameter. You try passing a lambda, or possibly a method, and create a CascadingParameter property, with matching name, of type Action or Func<...> or similar, to receive it. But the cascading parameter is always set to null.

Cascading parameters are matched up by Type, not by name, and who knows what the compiled type of a lambda will be? The C# reference tells you that that lambdas can be converted to delegates or expression trees but 'can be converted to' isn't good enough for matching up by Type.

The quickest fix

Declare your parameter to be of type Delegate. Most callable things in C# are of type Delegate.

// Declaration in the child component
[CascadingParameter]public Delegate OnChange { get; set; }

// Use in the child component. The null is what DynamicInvoke requires for 'no parameters'

# In the parent container
<CascadingValue Value="StateHasChanged" >

If that's not good enough

If you must distinguish between several Delegate parameters, or just want to better express intent in your code, you can define a named delegate:

public delegate void StateHasChangedHook();

and use that as the Type of your CascadingParameter:

// Declaration in the child component
[CascadingParameter]public StateHasChangedHook OnChange { get; set; }

// Use in the child component is slightly simpler

# In the parent container
<CascadingValue Value="(StateHasChangedHook)this.StateHasChanged" >

When you use named delegates, you can invoke them with onChange() syntax instead of .Invoke() or DynamicInvoke(null).

You can inspect what was set in the child parameter

log.LogDebug("OnChange was set {OnChange}.MethodInfo={MethodInfo}",OnChange,OnChange?.GetMethodInfo())

Recall that Action<> and Func<> are themselves named delegates, not types. They are declared in the System assembly as for instance: public delegate void Action<in T>(T obj)

.Net Core Strong Typed Configuration Binding for Arrays | Microsoft.Extensions.Configuration

The .Net Core Microsoft.Extensions.Configuration package includes the ConfigurationBinder.Get<>() method which can read a config file and bind name-value entries to a class. This gives you strongly typed configuration:

public class FromTo
    public string From { get; init; }
    public string To { get; init; }

The method is an extension method for Configuration so you can call it straight off a Configuration instance. In an Asp.Net Startup.cs class, for instance:


will result in your services collection knowing to provide an FromTo class with the Properties populated from the configuration entries, matched by section:propertyname :

  "fromto:from": "[email protected]",
  "fromto:to": "[email protected]"

or if you use secrets:

cd <projectdirectory>
dotnet user-secrets init
dotnet user-secrets set fromto:from [email protected]
dotnet user-secrets set fromto:to [email protected]

That works great for the usual primitives - string, numbers, boolean — but what about binding an Array?

public class FromTo
    public string From { get; init; }
    public string To[] { get; init; }

the From field is still bound but the To field silently fails and results in a null value.

The magic trick is to add a colon-separated number suffix to each setting name:

  "fromto:from": "[email protected]",
  "fromto:to:0": "[email protected]",
  "fromto:to:1": "[email protected]",
  "fromto:to:2": "[email protected]",

Now Configuration.GetSection("fromto").Get<FromTo>() will successfully bind all 3 rows for "fromto:to into the public string To[] array.

Add Merge Fields to a Word Document before adding a DataSource

Adding merge fields to a Word document without adding a datasource to it is not at all obvious when the mail merge button is greyed out. But to use MailMerge programmatically you probably want to do just that.

You can do it—unobviously—via Insert->QuickParts->Field:

Choose MergeField from the list on the left, and then you can type in your merge field name.

More recently, the “Field” button has been promoted (woo) to a top-level citizen of the Insert Ribbon, so now it' s a bit easier to find: