Dynamic Views in ASP.NET MVC 2

One of the new features introduced in C# 4.0 is the dynamic keyword. So far I haven’t had much use for it, but lately I’ve discovered that it can be very useful when designing ASP.NET MVC views. It also has uses when interacting with COM APIs and other dynamic languages, but I’m going to focus on its use in MVC.

If you’re using .NET 4.0 and ASP.NET MVC 2, and go to add a new view that is not strongly typed, you might notice that the first line will look like this:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

The thing to pay attention to here is that by default, non-strongly typed views now use a dynamic model by default. As with any use of dynamic you lose the compiler’s type checking in your view (if you’re not compiling your views I would recommend doing so), and your intellisense of course. That said, you do gain quite a bit of freedom when you only have to worry about the properties of the model, rather than the specific type.

Let’s use a very simple example to demonstrate a cool use of dynamic view models with a partial view. We will define a partial view that displays today’s date, and can take in a set of options about what aspects of the date to display. The options class is defined as:

public class DateDisplayOptions
{
    public bool ShowYear { get; set; }

    public bool ShowTime { get; set; }
}

Then we have the partial defined like this, using the options class as its view model:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DynamicViewSample.Models.DateDisplayOptions>" %>

<%: DateTime.Now.ToString("MMMM d") %>

<% if (Model.ShowYear)
   { %>
    <%: DateTime.Now.ToString(", yyyy")%>

<% } %>

<% if (Model.ShowTime)
   { %>

    <%: DateTime.Now.ToShortTimeString() %>

<% } %>

That will use the options to format today’s date and time appropriately. Now let’s say that we wanted to make it so that if you don’t pass an options object to the partial, it uses some default values. First, we add a constructor to DateDisplayOptions to set default values:

public DateDisplayOptions()
{
    ShowYear = true;
    ShowTime = false;
}

Next, we have to tell the view to allow for receiving a null view model:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DynamicViewSample.Models.DateDisplayOptions>" %>
<%@ Import Namespace="DynamicViewSample.Models" %>

<%
    DateDisplayOptions options = Model ?? new DateDisplayOptions();
%>

<%: DateTime.Now.ToString("MMMM d") %>

<% if (options.ShowYear)
   { %>

    <%: DateTime.Now.ToString(", yyyy")%>

<% } %>

<% if (options.ShowTime)
   { %>

    <%: DateTime.Now.ToShortTimeString() %>

<% } %>

Now passing in a null view model will display the year but not the time. However, this only gets us part of the way there. When using the RenderPartial method, not explicitly supplying a model to the partial will automatically send the model of the calling page. Obviously that will cause type casting problems when the calling page has a view model that is not DateDisplayOptions, so this is where the dynamic keyword comes to the rescue. We can change the partial to use the dynamic type, and then check for the type ourselves when casting:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<%@ Import Namespace="DynamicViewSample.Models" %>

<%
    DateDisplayOptions options = (Model as DateDisplayOptions) ?? new DateDisplayOptions();
%>

With that change, the partial will not care what type of model gets sent to it, but if it is not a DateDisplayOptions it will use a new instance with the default options instead. Let’s throw together a quick view to test everything out:

<p>
    <% Html.RenderPartial("ShowToday"); %>
</p>
<p>
    <% Html.RenderPartial("ShowToday", new DateDisplayOptions { ShowTime = false, ShowYear = false }); %>
</p>
<p>
    <% Html.RenderPartial("ShowToday", new DateDisplayOptions { ShowTime = true, ShowYear = true }); %>
</p>

That gives us the following output, which behaves exactly like we wanted:

dynamic view results

There are certainly a lot more uses out there for the dynamic keyword, but this is one example I stumbled upon where it came in very useful.

comments powered by Disqus
Navigation