ASP.NET MVC Dry UI

Reducing redundancy in ASP.NET MVC Views – There’s more than one way to do it.

DRY (Don’t Repeat Yourself) is a useful mantra when developing maintainable, readable code. ASP.NET MVC (with Razor view-engine) gives you multiple ways to remove unnecessary redundancy in your views.

Approach 1: @helper Methods

Although the @helper method syntax has been around a while and some of the other approaches below are more prevalent, @helper methods are still supported and may be a good solution for small bits of reusable view code.

The @helper method syntax gives you the ability to package up a section of markup and embeded C# server-side code in a reusable function that can be called from the view. When embedded in the view page itself it also has access to any of the page’s variables (including the Model).

Consider a view that contains three buttons, each of which has:

  • A title
  • Some explanatory text
  • An icon
  • A CSS class to apply colors
  • An MVC action that should be called when clicked

The desired output looks like this: Example with three buttons.

And here’s the markup to create it:

HelperInPage.cshtml
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<a href='@Url.Action("Action1")'
   class="buttonex buttonex-1">
    <div class="text-left">
        <i class="mdi mdi-comment-text"></i>
        <b>Button 1</b>
        <p class="mb-0">Description of first button</p>
    </div>
</a>

<a href='@Url.Action("Action2")'
   class="buttonex buttonex-2">
    <div class="text-left">
        <i class="mdi mdi-email-plus-outline"></i>
        <b>Button 2</b>
        <p class="mb-0">Description of second button</p>
    </div>
</a>


<a href='@Url.Action("Action3")'
   class="buttonex buttonex-3">
    <div class="text-left">
        <i class="mdi mdi-hexagon-slice-4"></i>
        <b>Button 3</b>
        <p class="mb-0">Description of third button</p>
    </div>
</a>

It’s pretty clear there is a lot of repetition going on here.

At the bottom of the view we can make use of the @helper syntax to create a re-usable function:

58
59
60
61
62
63
64
65
66
67
68
69
70
71
@helper ActionButton(
    string title, string description,
    string buttonCssClass, string iconCssClass,
    string action)
{
    <a href='@Url.Action(action)'
       class="buttonex @buttonCssClass">
        <div class="text-left">
            <i class="mdi @iconCssClass"></i>
            <b>@title</b>
            <p class="mb-0">@description</p>
        </div>
    </a>
}

Which simplifies our buttons down to this:

46
47
48
    @ActionButton("Button 1", "Description of first button", "buttonex-1", "mdi-comment-text", "Action1")
    @ActionButton("Button 2", "Description of second button", "buttonex-2", "mdi-email-plus-outline", "Action2")
    @ActionButton("Button 3", "Description of third button", "buttonex-3", "mdi-hexagon-slice-4", "Action3")

Approach 2: @helper Methods – Shared in App_Code

Helper methods don’t only have to live in a single view page. If you have a snippet that should be shared you can store this helper method definition in a Razor view page under App_Code.

  • Create a directory “App_Code” directly under the solution root (if it doesn’t already exist)
  • Create a file “MyHelpers.cshtml” for example.

Adding helpers to App_Code folder.

Move our ActionButton helper to the MyHelpers.cshtml page.


Note

One minor down-side of placing a @helper method in App_Code is that the UrlHelper and HtmlHelper classes are not available. You can overcome this in a couple of ways, but the simplest is to just pass in the Url helper from the caller.


App_Code/MyHelpers.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@using System.Web.Mvc

@helper ActionButton(
    string title, string description,
    string buttonCssClass, string iconCssClass,
    string action,
    UrlHelper Url) // Notice that UrlHelper is passed from the caller
{
    <a href='@Url.Action(action)'
       class="buttonex @buttonCssClass">
        <div class="text-left">
            <i class="mdi @iconCssClass"></i>
            <b>@title</b>
            <p class="mb-0">@description</p>
        </div>
    </a>
}

Which gives us this slightly modified usage in the view.

HelpersInAppCode.cshtml
15
16
17
18
19
20
@MyHelpers.ActionButton("Button 1", "Description of first button",
    "buttonex-1", "mdi-comment-text", "Action1", Url)
@MyHelpers.ActionButton("Button 2", "Description of second button",
    "buttonex-2", "mdi-email-plus-outline", "Action2", Url)
@MyHelpers.ActionButton("Button 3", "Description of third button",
    "buttonex-3", "mdi-hexagon-slice-4", "Action3", Url)

You can find more information about ASP.NET helper syntax here: Creating and Using a Helper in an ASP.NET Web Pages (Razor) Site

Approach 3: Partial Views

Partial views are .cshtml pages that are not used on their own, but are instead included as part of another page. One great use is for breaking apart a complicated or large page in to more managable units – rendering the Menu Bar, Side Bar, and Footer as separate components, for example.

Partial views can live either under a specific View’s directory, when used to decompose a complicated view in to smaller units, or under the Views/Shared directory, when used in multiple views. Partial views are named with a leading underscore to differentiate them.

One common use for partial views is to extract the common, repeated portions of a site (such as the main menu, sidebar, and footer) and refer to them in all pages. The main _Layout page is also a partial view.

Using Partial Views Figure.

As a simple example, consider a reusable partial view that renders messages.

Example of Message Partial View.

Create a view model to contain information about the message to be displayed, and store it under the Models directory.

Create MessageViewModel.

Models/MessageViewModel.cs
3
4
5
6
7
8
public class MessageViewModel
{
    public string Title { get; set; }
    public string Message { get; set; }
    public string CssClass { get; set; }
}

Create the partial view under Views/Shared so it can be re-used across pages.

Create Message.cshtml partial view.

Views/Shared/_Message.cshtml
1
2
3
4
5
6
7
8
9
@model ASPNETMVC_ex.Models.MessageViewModel
<div class="card mb-3">
    <div class="card-header @Model.CssClass">
        <b>@Model.Title</b>
    </div>
    <div class="card-body p-3">
        @Model.Message
    </div>
</div>

The _Message partial view can then be used to render messages.

@Html.Partial("_Message", new MessageViewModel
{
    Title = "Warning",
    Message = "The supplied machine ID is not valid.  Please check " +
        "the value and re-submit.",
    CssClass = "bg-danger text-light"
})

Approach 4: Display and Editor Templates

Display and editor templates are similar to partial views with a couple of differences.

  • By convention they live under Views/Shared in DisplayTemplates and EditorTemplates directories
  • Rather than using Html.Partial(), they are included using Html.DisplayFor() and Html.EditorFor()
  • They support model binding

Template Folders.

For example, consider an editor template used to edit percentages. We’d like it to bind to integer values from the model, and use Bootstrap 4 css classes for styling.

Percent Editor Template.

We can declare the editor template in the shared editor templates directory.

Views/Shared/EditorTemplates/Percent.cshtml
1
2
3
4
5
6
7
8
9
@model int

<div class="input-group" style="width: 100px; min-width: 0;">
    <input class="form-control text-right"
           type="number" value="@Model" />
    <div class="input-group-append">
      <div class="input-group-text">%</div>
    </div>
</div>

To use the template we’d call EditorFor with and bind to the model attribute containing the int value.

@Html.EditorFor(m => m.BatteryChargeLevel, "Percent", null)

Conclusion

Because ASP.NET MVC and the Razor view engine gives you so many choices it is sometimes hard to choose the right way to re-use UI code. Hopefully the above examples will help you choose the best fit for each situation you encounter.

Next
Previous