How to change the default ASP.NET MVC theme

If you've come across this post, it's most likely that you're scouring the web for how to change the "theme" in an ASP.NET MVC project. That's intentional. I see this question a lot, so rather than fight against the tide, I figured it's best to embrace the confused terminology and resteer the boat from there.

First, ASP.NET MVC doesn't have themes. ASP.NET MVC is a framework, not a content management system. With a theme you're bound to a certain pre-defined system. With a framework, you define that system. That means you have more work to do, but it also means you have complete control. So, while I tricked you a bit to get you here, I promise that I will still help you achieve what you want, just perhaps not quite in the way you thought or imagined.

Here we go.

Razor

MVC comes with a template engine called Razor. What's a template engine, you ask? It's a preprocessor that runs over your view code (generally, HTML), extracting items considered to be blocks of code (based on a particular syntax that the engine recognizes), executing those items, and compiling the result of that code with the other static bits of your view to return a rendered document.

That's a bit obtuse, so simply let's just say Razor allows you to add dynamic content to what otherwise would be just static content, such as HTML. For example, you might want to repeat a bit of HTML for each item in a list. With a variable-length list, that would be impossible to achieve with HTML alone, but with HTML plus Razor, it's easily doable.

Now, as I mentioned, a template engine bases its determination of what is and is not "code" on a syntax that it recognizes. So, let's take a quick look at Razor's syntax.

@

Razor uses an @ to designated a block of code. However, it comes in various forms:

  • @someVariable or @someMethod()

    This form indicates that Razor should print the result directly. In the case of a variable, Razor will just output the value of that variable. In the case of a method, Razor will output the return value of that method.

  • @{ ... }

    Curly braces indicate a code block. Inside the braces you can put any valid C#, VB, etc. code. However, nothing is output to the result.

  • @( ... )

    Parenthesis indicate that the return value of the code inside should be output to the result. This is best used with expressions, such as a ternary, e.g. @(someBoolean ? "Yes" : "No"). It's also very handy when Razor can't quite figure out what it should handle and what it shouldn't. For example, with something like: [email protected], Razor would think it needed to look for a variable named someVarsomething, whereas what you really wanted was someVar. The confusion can be removed by changing it to something@(someVar)something.

  • @:

    Literal text. Inside a code a code block, or if you just want to tell Razor to leave a particular line alone, you can start the line with @: and Razor will treat anything else on that line as literal text that should be just directy output to the result.

  • @@

    Output a literal @. Since Razor interprets all the @s as code designators, you need some way to actually just have an @, if that's what you wanted. For example: joebob@@email.com would result in [email protected].

Layouts, Views and Partials

Razor processes views. A view is merely a text file with a specialized extension that Razor recognizes as something it should do some work on. For C# projects, views will have the extension .cshtml, while in VB projects, views have the extension .vbhtml. The extension indicates what language Razor should process the code in. For the purposes of simplicity and since C# is the most common language used to develop MVC projects, I will refer to views with the .cshtml extension. Just note that if you're using VB, you'll look for or use the .vbhtml extension.

A layout is just a view. It sounds a little like circular logic but really the best definition of a layout is "a view used as the layout for another view". It's the act of using it as a layout that makes it a layout, and it's really just a semantic thing. Razor, like the honey badger, doesn't care. However, there are certain things you would do inside a layout that really make it work as a layout (we'll cover that in a bit). Without doing those things, just setting a view as a layout wouldn't really do anything, but you also wouldn't get an error.

Partials or partial views are merely views without any layout. They are generally pulled into a full-fledged view to allow reusability of bits of HTML or logic, though they can be used on their own as well.

So lets look at a quick comparison to cement the differences:

  • Layout: a view used as a layout
  • View: a view that uses a layout
  • Partial: a view that doesn't use a layout

Layouts

As I mentioned before, a layout is just a view, but there's certain things you will add to a layout to make it actually do something useful. If you look in the Views\Shared folder of a new MVC project, you'll find a view inside called _Layout.cshtml. This is the default layout for a new project, and will serve as a jumping off point for your site's template(s). There's nothing magical about this particular file or its name, though, you could replace its contents with something else entirely if you wanted. The only required thing to make it still function as a layout, though, is the somewhat magical @RenderBody(). This method tells Razor that this point is where you want to output the result of any view that uses this layout. Let's start with a really basic example. Given:

_Layout.cshtml

@RenderBody()

MyView.cshtml

@{ Layout = "~/Views/Shared/_Layout.cshtml"; }
Hello World!

Rendering MyView.cshtml would result in a page with nothing but the text "Hellow World!". Don't worry too much about the first line of MyView.cshtml. We'll look more at that in a second.

Layouts can also utilize sections. Sections are basically placeholders for content you may or may not want to provide in your view. As an example, let's say that you wanted to be able to add additional scripts right before the closing body tag, on a per-view basis:

_Layout.cshtml

<html>
    <head>
        <title>My Awesome Site</title>
    </head>
    <body>
        @RenderBody()
        @RenderSection("scripts", required: false)
    </body>
</html>

Then, in your view, you can use the special @section Razor keyword to specify content to be placed where you call RenderSection in your layout:

@section scripts
{
    <script src="awesome.js"></script>
}

Cascading Layouts

Often, you'll want a single common base layout and then some more specialized layouts that "inherit" from that base layout. Razor can handle this easily, with one big caveat: you must implement any defined sections in the base layout in your sublayout. Using the layout example above, if I wanted to have a two-column layout, I could do:

_TwoColumnLayout.cshtml

@{ Layout = "~/Views/Shared/_Layout.cshtml"; }
<div class="container">
    <div class="content">
        @RenderBody()
    </div>
    <div class="sidebar">
        @RenderSection("sidebar", required: true)
    </div>
</div>

@section scripts {
    @RenderSection("scripts", required: false)
}

Let's take a close look at this. First, we specify that this uses our main layout. Layouts are just views, so they can use their own layouts just like any view can. Because of that, we don't need to include any of the boilerplate HTML markup. That's covered by _Layout.cshtml. Just like a normal view, the results of this will just be placed where RenderBody is called in the base layout. Notice, though, that our sublayout also has a call to RenderBody. The result of whatever view uses this layout will be inserted there, and then the result of this sublayout, including the rendered view, will be inserted into the base layout.

We've also added a new section, called "sidebar". Notice that this time, we passed required: true versus required: false. As the name indicates, this specifies whether the section is required or optional. If you fail to implement a required section, a runtime exception will be raised, while an optional section is, well, optional: you can choose to implement it or not.

Finally, we implement the "scripts" section from the base layout. Inside, we merely call RenderSection again, but you could just put a static implementation if you wanted. If you did that, then any views that use this layout would not be able to also implement the "scripts" section. Or, you could leave it completely empty, which effectively just disables the section entirely. You may also add static content and a call to RenderSection, which allows you to layer the section. For example, you might want to create a _StoreLayout and include store.js along with any view that uses this layout, while still allowing the individual views to add additional scripts:

@section scripts {
    <script src="store.js"></script>
    @RenderSection("scripts", required: false)
}

You can create sublayouts of sublayouts of sublayouts, as well. There's no limit to how many nested layouts you have, though it's very atypical to need more than 1-2 layers. You just need to remember to re-implement any and all sections from ancestor layouts, or you'll lose that section.

Views

As explained earlier, a "view" is a view that uses a layout. You specify a layout by storing a string containing a path to the layout in the special Layout variable, as you've seen a few times now, already. However, I'm sure you could imagine how tedious this would be if you had to do it for every single view. Thankfully, Razor provides a better way. There's a special view called _ViewStart.cshtml, and if this view is present, any views in the same folder or any subfolders within that folder will use whatever layout is specified there. This is very powerful, as it allows you to specify the layout for a larger number of views at once and also override that on a folder by folder basis.

To specify the layout for all views for your entire project, add a new file named _ViewStart.cshtml to the Views folder with the following contents:

_ViewStart.cshtml

@{ Layout = "~/Views/Shared/_Layout.cshtml"; }

Or, you could use a completely different layout here. Again, the name of the layout files don't matter; as long as it's a path to a real view, you're golden.

Later, if you added that new ecommerce store to your site, you could create another _ViewStart.cshtml under Views\Store and set Layout to _StoreLayout.cshtml there.

Conventions

This is a good place to talk about some conventions that Razor uses. First, all views are expected to be in the Views folder of your project. Then, Razor looks for a folder in Views that matches the name of the controller whose action was called, minus the "Controller" part. So, if I had a controller named StoreController, Razor would expect views for that controller to be inside Views\Store. Next, the actual name of the view is expected to be the same as the action that was called. So, for the Index action of my StoreController, Razor would look for the view Views\Store\Index.cshtml. All of these conventions can be overridden (though some with more difficulty than others), but it's best to just follow the conventions. Any ASP.NET MVC developer will expect to find an Index.cshtml view for an Index action, and if it's not there, it will just cause confusion.

Another convention is that Views\Shared folder we've seen used for all our layouts thus far. As the name indicates, this is where you should put views that are shared between multiple controllers. Razor will always fallback to this folder if it can't find a particular view where it expects it to be.

Partial Views

A told you a partial view is just a view without a layout, but that's not exactly true. Actually, there's no difference at all between a view and a partial view on its own. The difference comes in how it's used, much as a layout is mostly a layout because it's used as a layout. Partial views are most often used in conjunction with the Razor helpers, Html.Partial or Html.RenderPartial, and it's those methods, rather than the partial view itself that determine that no layout will be used. Both methods achieve the same result, the only difference is that Html.Partial directly writes the output whereas Html.RenderPartial returns a string. Generally, you'll just use Html.Partial unless you have a good reason to use the other.

Going back to our two-column layout, let's say we wanted to create a partial view that contains the standard sidebar pretty much every two-column view will use:

Views\Shared_Sidebar.cshtml

<ul>
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>
</ul>

I'm not specifying any real links here just for simplicities sake. Now, we can use this in a view that uses our two-column layout:

MyView.cshtml

@{ Layout = "~/Views/Shared/_TwoColumnLayout.cshtml"; }

<p>This is my view.</p>

@section sidebar
{
    @Html.Partial("_Sidebar")
}

I went ahead and explicitly set the layout here to make it clear what layout the view uses, but remember that this can be set in a _ViewStart.cshtml file instead. It's also worth noting that if we had specified a layout in _ViewStart.cshtml, a layout explicitly set in a view takes precedence. This allows you to customize the layout on just one particular view, even if all the other views in the same folder are using a different layout.

Then, we implement the "sidebar" section with a call to Html.Partial. Notice here that I only specified the view name. Razor will fallback to looking for it in the Views\Shared folder and will even add the .cshtml extension for us. You could also specify a full path to the partial view if you so desired. And, again, it's because we used Html.Partial that Razor treats this as a partial view and does not add any layout specified in _ViewStart.cshtml.

View Overrides

I've mentioned a few times now that Razor will fallback to Views\Shared if it can't find an appropriate view in the folder named after the controller. This is actually a really great feature, because it allows you to override views. Let's say that for our StoreController views, we wanted to use a completely different sidebar. Instead of going into each view and changing the partial reference to something like "_StoreSidebar", we can instead add a new partial view with the same _Sidebar.cshtml name to Views\Store. Then, for any store action, Views\Store\_Sidebar.cshtml would match before Views\Shared\_Sidebar.cshtml and be included instead. Pretty nice, huh?

When a Partial View Is Not a Partial View

As a fun excercise let's see what would happen if we had an action like the following:

public ActionResult NotAPartialView()
{
    return View("_Sidebar");
}

Because we're returning a ViewResult here, Razor will go ahead and wrap our "partial view" with whatever layout _ViewStart.cshtml dictates. We could fix this by returning a PartialViewResult instead:

public ActionResult APartialView()
{
    return PartialView("_Sidebar");
}

This is mostly just to illustrate a point and isn't something you would do typically, but it can be handy for returning partial HTML responses for AJAX requests. Just remember that a partial view is only a partial view if you treat it as one.

_WhyAreSomeViewsPrefixedThisWay.cshtml

Just one further note about the use of _ as a prefix for layouts and partial views. There's nothing magicical about the underscore. It's just a convention to denote that somehow these are not quite your normal run-of-the-mill views. You don't have to include the underscore, but I recommend that you do. Not only does it serve as quick way to see which views are standard views and which are partials or layouts, but it also serves to order them together in your project tree.

comments powered by Disqus