ASP.NET MVC · Controllers & Views

Strongly Typed Views

A strongly typed view knows exactly what data it receives. The compiler checks your property names, the editor autocompletes them, and refactoring stops breaking pages silently.

Intermediate 6 min Lesson 13 of 37Covers ASP.NET MVC 5.2 on .NET Framework 4.8.
0/37 in ASP.NET MVC
On this page · 6 min · Intermediate

Learning objectives#

  • Declare a view’s model type with @model
  • Design a view model for one page
  • Use DisplayFor and DisplayNameFor with [Display] and [DisplayFormat]
  • Explain why view models are better than entities and ViewBag

Explanation#

@model OrderSummaryViewModel at the top of a view makes the Model property that type. Model.Total is checked when the view compiles; Model.Totl is an error, not a silent blank.

A view model is a class made for one view: it contains exactly what the page shows, already shaped (CustomerName instead of Customer.FirstName + LastName). The controller builds it from entities.

Lambda-based helpers read metadata from the model: Html.DisplayNameFor(m => m.PlacedOn) shows the [Display(Name = "Placed on")] label, and Html.DisplayFor applies [DisplayFormat(DataFormatString = "{0:dd MMM yyyy}")].

View models protect you: the view cannot accidentally show or post fields you did not include (like IsAdmin or PasswordHash), and changes to the database schema do not ripple into every view.

Syntax#

C# Illustrative
public class OrderSummaryViewModel
{
    [Display(Name = "Order number")] public int Id { get; set; }
    [DisplayFormat(DataFormatString = "{0:N2}")] public decimal Total { get; set; }
}


@model OrderSummaryViewModel
@Html.DisplayNameFor(m => m.Total): @Html.DisplayFor(m => m.Total)

Code example: An order summary page with a view model#

C#OrderSummaryViewModel.cs Illustrative
// Models/OrderSummaryViewModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;


namespace ShopSite.Models
{
    public class OrderSummaryViewModel
    {
        [Display(Name = "Order number")]
        public int Id { get; set; }


        [Display(Name = "Customer")]
        public string CustomerName { get; set; }


        [Display(Name = "Placed on")]
        [DisplayFormat(DataFormatString = "{0:dd MMM yyyy}")]
        public DateTime PlacedOn { get; set; }


        [DisplayFormat(DataFormatString = "{0:N2}")]
        public decimal Total { get; set; }


        public List<string> Items { get; set; }
    }
}


// Controllers/OrdersController.cs
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using ShopSite.Models;


namespace ShopSite.Controllers
{
    public class OrdersController : Controller
    {
        public ActionResult Summary(int id)
        {
            // In a real app this is mapped from the Order and Customer entities
            var model = new OrderSummaryViewModel
            {
                Id = id,
                CustomerName = "Asha Patil",
                PlacedOn = new DateTime(2026, 10, 27),
                Total = 1234.5m,
                Items = new List<string> { "Gel Pen x3", "Notebook x2" },
            };
            return View(model);
        }
    }
}


@* Views/Orders/Summary.cshtml *@
@model OrderSummaryViewModel
<h1>@Html.DisplayNameFor(m => m.Id) @Model.Id</h1>
<dl>
    <dt>@Html.DisplayNameFor(m => m.CustomerName)</dt>
    <dd>@Html.DisplayFor(m => m.CustomerName)</dd>
    <dt>@Html.DisplayNameFor(m => m.PlacedOn)</dt>
    <dd>@Html.DisplayFor(m => m.PlacedOn)</dd>
    <dt>@Html.DisplayNameFor(m => m.Total)</dt>
    <dd>@Html.DisplayFor(m => m.Total)</dd>
</dl>
<p>@Model.Items.Count items: @string.Join(", ", Model.Items)</p>
Expected output
GET /Orders/Summary/5001
200 OK
<h1>Order number 5001</h1>
<dl>
    <dt>Customer</dt>
    <dd>Asha Patil</dd>
    <dt>Placed on</dt>
    <dd>27 Oct 2026</dd>
    <dt>Total</dt>
    <dd>1,234.50</dd>
</dl>
<p>2 items: Gel Pen x3, Notebook x2</p>

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. @model OrderSummaryViewModel gives the view a typed Model and lambda helpers.
  2. DisplayNameFor reads [Display(Name)]; Total has no Display attribute, so its property name is used.
  3. DisplayFor applies [DisplayFormat]: the date becomes 27 Oct 2026 and the total 1,234.50.
  4. The view model already has CustomerName as one string, so the view does not need to know how names are stored.

Real-world use case#

A bank’s statement page shows formatted dates and amounts using DisplayFor with [DisplayFormat] on the view model. When the format changes to show currency codes, only the attribute changes — every view using DisplayFor updates.

Common mistakes#

Passing EF entities straight to views

Entities often contain fields you must not show or accept, and lazy-loaded navigation properties can trigger database queries while the view renders. Map to a view model.

Model type mismatch

Passing a List<Product> to a view declared @model Product throws “The model item passed into the dictionary is of type…”. Check what the action passes.

Using ViewBag for the main data

ViewBag.Products works until someone renames it in one place. A typed model fails at compile time instead.

Best practices#

  • One view model per page (or per form)
  • Keep formatting in [DisplayFormat] and DisplayFor
  • Map entities to view models in the controller or a mapper
  • Turn on MvcBuildViews in release builds to catch view errors at compile time

Remember this#

@model = typed Model + IntelliSense.

View model = exactly what the page needs.

DisplayNameFor/DisplayFor read [Display]/[DisplayFormat].

Practice exercise#

Your task: Create a ProfileViewModel with FullName, JoinedOn (format yyyy-MM-dd) and Points (format N0, label “Reward points”) and render it with DisplayNameFor and DisplayFor.

Need a hint?
Use [Display(Name = "Reward points")] and [DisplayFormat(DataFormatString = "{0:N0}")].
Show solution
C# Illustrative
// Models/ProfileViewModel.cs
using System;
using System.ComponentModel.DataAnnotations;


namespace ShopSite.Models
{
    public class ProfileViewModel
    {
        [Display(Name = "Name")]
        public string FullName { get; set; }


        [Display(Name = "Member since")]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
        public DateTime JoinedOn { get; set; }


        [Display(Name = "Reward points")]
        [DisplayFormat(DataFormatString = "{0:N0}")]
        public int Points { get; set; }
    }
}


// Controllers/ProfileController.cs
using System;
using System.Web.Mvc;
using ShopSite.Models;


namespace ShopSite.Controllers
{
    public class ProfileController : Controller
    {
        public ActionResult Index() => View(new ProfileViewModel { FullName = "Ravi Kumar", JoinedOn = new DateTime(2024, 1, 15), Points = 12500 });
    }
}


@* Views/Profile/Index.cshtml *@
@model ProfileViewModel
<p>@Html.DisplayNameFor(m => m.FullName): @Html.DisplayFor(m => m.FullName)</p>
<p>@Html.DisplayNameFor(m => m.JoinedOn): @Html.DisplayFor(m => m.JoinedOn)</p>
<p>@Html.DisplayNameFor(m => m.Points): @Html.DisplayFor(m => m.Points)</p>
Expected output
GET /Profile
200 OK
<p>Name: Ravi Kumar</p>
<p>Member since: 2024-01-15</p>
<p>Reward points: 12,500</p>

Mini quiz#

Strongly Typed Views quizMultiple choice Question 1 of 3

Which directive makes a view strongly typed?

Score so far: 0/0

Want more? Practise all ASP.NET MVC MCQs · ASP.NET MVC interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.