This site is archived. I now update and maintain my blog here.

Typed access to Umbraco Marco parameters in Partial Views

In an Umbraco PartialViewMacroPage any Macro parameters are typed as:

IDictionary<string, object>

This makes for some rather messy code when trying to retrieve these parameters. Consider a Partial View Macro that takes two parameters:

Date - any date

Days - a number of days to subtract from the date

The purpose of this Macro is going to be to output the following using Razor:

@days days before @date.ToString("dd MMM") ago it was @date.Subtract(TimeSpan.FromDays(days)).ToString("dd MMM")

You'd register the parameters above with Umbraco and render the Macro as follows.

@Umbraco.RenderMacro("DaysFromDate", new { days = 10, date = "2014/01/01" })

Note: one could pass the date parameter as a DateTime rather than a string, but as we'll see it ends up being passed to our view as a string anyway.

So now, here is the code to retrieve the values of our parameters in the partial view:

var days = string.IsNullOrEmpty((string) Model.MacroParameters["days"]) ? 0 : Convert.ToInt32(Model.MacroParameters["days"]); var date = string.IsNullOrEmpty((string) Model.MacroParameters["date"]) ? DateTime.Now : DateTime.Parse((string) Model.MacroParameters["date"]);

Pretty nasty, but we have to do the above because:

If a parameter is registered on a Macro but not passed then we get an empty string

If the parameter is passed it is passed to us as a string and we need to do the conversion

A dig around the Umbraco core reveals the following that will assist us with type conversion:

var attemptDate = Model.MacroParameters["date"].TryConvertTo(typeof(DateTime)); var actualDate = attemptDate.Success ? (DateTime) attemptDate.Result : DateTime.Now;

So still a bit verbose for me, so we use extension methods that allow us to do the following:

var date = Model.MacroParameters.GetValue("date", DateTime.Now); var days = Model.MacroParameters.GetValue("days", 2);

Note: the return type is implied by the type of the second argument (which is the default value if no macro parameter is passed). You can also use the extension methods in the following form:

var date = Model.MacroParameters.GetValue<DateTime>("date", DateTime.Now); var days = Model.MacroParameters.GetValue<int>("days");

The first example above demonstrates that you can pass a type for readability even though it is implicit - the second, shows that you can use the method without a specified default value in which case you get the default value for the given type.

So finally the definitions for the extension methods:

public static class UmbracoExtensions { public static T GetValue<T>(this IDictionary<string, object> dictionary, string key) { return dictionary.GetValue(key, default(T)); } public static T GetValue<T>(this IDictionary<string, object> dictionary, string key, T defaultValue) { if (!dictionary.ContainsKey(key) || string.IsNullOrEmpty(dictionary[key].ToString())) return defaultValue; return (T)Convert.ChangeType(dictionary[key], typeof(T)); } }

I'd like these in the Umbraco core. Would you?