Table of contents

Last updated .

< Previous chapterNext chapter >

Views

The examples from the previous two chapters didn't render html - they just spew out a bit of text, returned from a controller method. To produce some html, a View is needed.

I can modify the HomeController.Index method to specify that a view should be rendered:

Controllers/HomeController.cs (partial)
1:
2:
3:
4:
public ViewResult Index() { return View(); }

Now, instead of returning a string, the method returns a ViewResult by calling the View method of the base Controller class. If you were to request the Index method, you'd get a blank page, because we haven't created that view object yet.

A view defines the UI of a page or part of a page. In a view file, static html markup is interspersed with code blocks and expressions that provide dynamic page content. The file extension cshtml reflects that. The MVC framework is designed so that a view file is not accessible from the outside. The browser can only get a view by accessing a controller, which in turn can deliver the view. Let's create a minimalistic view for the HomeController.Index method. By convention, views should go in a Views folder, so first create that folder. Another convention says that views related to a particular controller class should be located in a folder named after the controller class. We are about to create a view for the HomeController.Index method, so create a new Home folder as a child of the Views folder. Now, right-click the Home folder and go Add > New Item..., then select MVC View Page from the list of templates and name the file Index.cshtml:

Add a view
Add a view

By naming the view Index we have just created the default view for the action of the same name. To keep things simple for now, I added just the minimal html skeleton plus a header with some text:

Views/Home/Index.cshtml
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
@* View for the Home/Index action *@ <!doctype html> <html> <head> <title>HomeController.Index</title> </head> <body> <h1>Hello from HomeController.Index !</h1> </body> </html>
The view for the Index action
The rendered view for the Index action

Now, when the Index action is requested, it causes the Index view to be rendered, even though the code doesn't explicitly specify which view to render. In this case, MVC chooses the default view, which is the view with the same name as the action, located in a folder with the same name as the controller (without the "controller") suffix.

The view object just created looks okay, but it's completely static. It would be nice if we could make it dynamic, so that the controller could inject some data into it. Otherwise, we might as well use a static html file. One way to accomplish that is by using the Controller.ViewData object, which is a specialized dictionary. The view has access to the same object (via the ViewData property of the parent RazorPage class). With that, the controller can add any kind of object to this dictionary, indexed by a string name and it is then available for the view to use. Actually, there is an alternative syntax for this, involving the ViewBag property, which is of type dynamic. With that, you can add the data as a named property of the ViewBag. It doesn't matter if you use ViewData or ViewBag - they access the same dictionary. I modified the controller to use these constructs to add two pieces of data to the dictionary:

Controllers/HomeController.cs (partial)
1:
2:
3:
4:
5:
6:
public ViewResult Index() { ViewData["Title"] = "HomeController.Index"; ViewBag.Time = DateTime.Now.ToString(); return View(); }

Now, the view can be updated to make use of these properties:

Views/Home/Index.cshtml
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
@* View for the Home/Index action *@ <!doctype html> <html> <head> <title>@ViewBag.Title</title> </head> <body> <h1>Hello from HomeController.Index !</h1> <p>Server time is @ViewBag.Time</p> </body> </html>
The rendered view with server time
The rendered view with server time

The sample shown above introduces Razor expressions and code blocks, identified by the leading "@". A code block is simply some lines of code, while an expression is a line of code that returns a value. In the case of an expression, the return value is what ends up in the resulting html. A Razor expression is identified with a leading @ sign, and the expression is optionally wrapped in parentheses; for example:

@ViewBag.PageTitle

or the equivalent

@(ViewBag.PageTitle)

The parantheses are sometimes necessary in order to disambiguate the intent. A code block is wrapped in curly parantheses and with a leading @ sign, as shown above. Visual Studio is nice enough to indicate with background colors what is code and what is markup. The aboce code also shows the syntax for adding a comment. A lot more can be said about Razor syntax and I will come back to that.

More on ViewData and ViewBag

There is one caveat to be aware of: using the ViewBag requires that the string used for the key does not contain characters that are invalid in a c# identifier, such as a space or hyphen. For the same reason, the key cannot be a c# keyword such as "class". The ViewBag property is typed as dynamic, so there is no intellisense support in the editor and no compile-time syntax check. Also, since the values in the dictionary are of the general Object type, view code using ViewData often needs to cast to the "real" type.

In so far as the controller class goes, it too inherits ViewData and ViewBag properties from its parent Microsoft.AspNet.Mvc.Controller class. The MVC framework takes care of passing this dictionary object from the controller to the view.

The whole point of ViewData is to enable the controller to pass data to the view, which can then form part of the rendered page, as the above example demonstrates. However, ViewData represents a fairly unstructured scheme that does not scale well to real scenarios with lots of structured data. And it has its practical drawbacks, as outlined previously.

Strongly typed views

Instead of relying on ViewData to pass data from controller to the view, a custom model object can be passed from the controller to the view. A special @model statement in the view then makes it possible to state the type of that object and thus gain the benefits of intellisense and type safety. In order to modify the controller and view to use a model object, I first have to define a model, so I wrote a simple Contact class for the model. I added it to a new Models folder, which is where model objects should go:

Models/Contact.cs
1:
2:
3:
4:
5:
6:
7:
8:
9:
namespace MVCTutorial.Models { public class Contact { public long ID { get; set; } public string Name { get; set; } public string Email { get; set; } } }

Now, the HomeController class can be modified to create a list of a few arbitrary contacts and pass it to the view via the View method. This list becomes the model object, which is then accessible in the View through the Model property:

Controllers/HomeController.cs
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
using Microsoft.AspNet.Mvc; using MVCTutorial.Models; namespace MVCTutorial.Controllers { public class HomeController : Controller { public ViewResult Index() { ViewData["Title"] = "Contacts"; Contact[] contacts = new Contact[] { new Contact { ID = 1, Name = "Albert Einstein", Email = "alei@princeton.org" }, new Contact { ID = 2, Name = "Niels Bohr", Email = "nibo@nbinstitute.ku.dk" } }; return View(contacts); } } }

Now, the view can declare that it expects an array of Contact objects as its data model and then make use of that object to enumerate a list of object as an unordered list:

Views/Home/Index.cshtml
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
@* View for the Home/Index action *@ @using MVCTutorial.Models @model Contact[] <!doctype html> <html> <head> <title>@ViewBag.Title</title> </head> <body> <h1>Hello from HomeController.Index !</h1> <h2>Contacts:</h2> <ul> @foreach (Contact contact in Model) { <li>@contact.Name: @contact.Email</li> } </ul> </body> </html>
The rendered view with contacts
The rendered view with contacts

Notice that the namespace for the Contact class is introduced with a @using statement and the model type is stated using a @model directive. In the body element, a block of code iterates through the contacts in the list (contained in the Model property) and renders each one as an item in an unordered list.

The resulting view in the browser isn't exactly impressive, but it drives home the point of using a model object to pass data from a controller to a view. We have now seen the basic parts of what constitutes the MVC pattern: A request is funnelled to a Controller class, which then manipulates a Model object, which it then passes to a View, which renders the model data.

< Previous chapterNext : A simple data entry app >


2016 by Niels Hede Pedersen Linked in