[c#] ASP.NET MVC Razor pass model to layout

What I see is a string Layout property. But how can I pass a model to layout explicitly?

This question is related to c# .net asp.net-mvc razor

The answer is


  1. Add a property to your controller (or base controller) called MainLayoutViewModel (or whatever) with whatever type you would like to use.
  2. In the constructor of your controller (or base controller), instantiate the type and set it to the property.
  3. Set it to the ViewData field (or ViewBag)
  4. In the Layout page, cast that property to your type.

Example: Controller:

public class MyController : Controller
{
    public MainLayoutViewModel MainLayoutViewModel { get; set; }

    public MyController()
    {
        this.MainLayoutViewModel = new MainLayoutViewModel();//has property PageTitle
        this.MainLayoutViewModel.PageTitle = "my title";

        this.ViewData["MainLayoutViewModel"] = this.MainLayoutViewModel;
    }

}

Example top of Layout Page

@{
var viewModel = (MainLayoutViewModel)ViewBag.MainLayoutViewModel;
}

Now you can reference the variable 'viewModel' in your layout page with full access to the typed object.

I like this approach because it is the controller that controls the layout, while the individual page viewmodels remain layout agnostic.

Notes for MVC Core


Mvc Core appears to blow away the contents of ViewData/ViewBag upon calling each action the first time. What this means is that assigning ViewData in the constructor doesn't work. What does work, however, is using an IActionFilter and doing the exact same work in OnActionExecuting. Put MyActionFilter on your MyController.

public class MyActionFilter: Attribute, IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            var myController= context.Controller as MyController;

            if (myController!= null)
            {
                myController.Layout = new MainLayoutViewModel
                {

                };

                myController.ViewBag.MainLayoutViewModel= myController.Layout;
            }
        }
    }

this is pretty basic stuff, all you need to do is to create a base view model and make sure ALL! and i mean ALL! of your views that will ever use that layout will receive views that use that base model!

public class SomeViewModel : ViewModelBase
{
    public bool ImNotEmpty = true;
}

public class EmptyViewModel : ViewModelBase
{
}

public abstract class ViewModelBase
{
}

in the _Layout.cshtml:

@model Models.ViewModelBase
<!DOCTYPE html>
  <html>
  and so on...

in the the Index (for example) method in the home controller:

    public ActionResult Index()
    {
        var model = new SomeViewModel()
        {
        };
        return View(model);
    }

the Index.cshtml:

@model Models.SomeViewModel

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

<div class="row">

i disagree that passing a model to the _layout is an error, some user info can be passed and the data can be populate in the controllers inheritance chain so only one implementation is needed.

obviously for more advanced purpose you should consider creating custom static contaxt using injection and include that model namespace in the _Layout.cshtml.

but for basic users this will do the trick


Let's assume your model is a collection of objects (or maybe a single object). For each object in the model do the following.

1) Put the object you want to display in the ViewBag. For example:

  ViewBag.YourObject = yourObject;

2) Add a using statement at the top of _Layout.cshtml that contains the class definition for your objects. For example:

@using YourApplication.YourClasses;

3) When you reference yourObject in _Layout cast it. You can apply the cast because of what you did in (2).


Maybe it isnt technically the proper way to handle it, but the simplest and most reasonable solution for me is to just make a class and instantiate it in the layout. It is a one time exception to the otherwise correct way of doing it. If this is done more than in the layout then you need to seriously rethink what your doing and maybe read a few more tutorials before progressing further in your project.

public class MyLayoutModel {
    public User CurrentUser {
        get {
            .. get the current user ..
        }
    }
}

then in the view

@{
    // Or get if from your DI container
    var myLayoutModel = new MyLayoutModel();
}

in .net core you can even skip that and use dependency injection.

@inject My.Namespace.IMyLayoutModel myLayoutModel

It is one of those areas that is kind of shady. But given the extremely over complicated alternatives I am seeing here, I think it is more than an ok exception to make in the name of practicality. Especially if you make sure to keep it simple and make sure any heavy logic (I would argue that there really shouldnt be any, but requirements differ) is in another class/layer where it belongs. It is certainly better than polluting ALL of your controllers or models for the sake of basically just one view..


public interface IContainsMyModel
{
    ViewModel Model { get; }
}

public class ViewModel : IContainsMyModel
{
    public string MyProperty { set; get; }
    public ViewModel Model { get { return this; } }
}

public class Composition : IContainsMyModel
{
    public ViewModel ViewModel { get; set; }
}

Use IContainsMyModel in your layout.

Solved. Interfaces rule.


There is another way to archive it.

  1. Just implement BaseController class for all controllers.

  2. In the BaseController class create a method that returns a Model class like for instance.

public MenuPageModel GetTopMenu() 
{    

var m = new MenuPageModel();    
// populate your model here    
return m; 

}
  1. And in the Layout page you can call that method GetTopMenu()
@using GJob.Controllers

<header class="header-wrapper border-bottom border-secondary">
  <div class="sticky-header" id="appTopMenu">
    @{
       var menuPageModel = ((BaseController)this.ViewContext.Controller).GetTopMenu();
     }
     @Html.Partial("_TopMainMenu", menuPageModel)
  </div>
</header>

From above only, but simple implementation

Index Page

@model CMS.Models.IndexViewModel 

@{
    ViewBag.PageModel = Model;    
}

Layout Page

@{
    var Model = (CMS.Models.IndexViewModel)ViewBag.PageModel;        
}

Why dont you just add a new Partial View with i's own specific controller passing the required model to the partial view and finally Render the mentioned partial view on your Layout.cshtml using RenderPartial or RenderAction ?

I use this method for showing the logged in user's info like name , profile picture and etc.


old question but just to mention the solution for MVC5 developers, you can use the Model property same as in view.

The Model property in both view and layout is assosiated with the same ViewDataDictionary object, so you don't have to do any extra work to pass your model to the layout page, and you don't have to declare @model MyModelName in the layout.

But notice that when you use @Model.XXX in the layout the intelliSense context menu will not appear because the Model here is a dynamic object just like ViewBag.


For example

@model IList<Model.User>

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

Read more about the new @model directive


A common solution is to make a base view model which contains the properties used in the layout file and then inherit from the base model to the models used on respective pages.

The problem with this approach is that you now have locked yourself into the problem of a model can only inherit from one other class, and maybe your solution is such that you cannot use inheritance on the model you intended anyways.

My solution also starts of with a base view model:

public class LayoutModel
{
    public LayoutModel(string title)
    {
        Title = title;
    }

    public string Title { get;}
}

What I then use is a generic version of the LayoutModel which inherits from the LayoutModel, like this:

public class LayoutModel<T> : LayoutModel
{
    public LayoutModel(T pageModel, string title) : base(title)
    {
        PageModel = pageModel;
    }

    public T PageModel { get; }
}

With this solution I have disconnected the need of having inheritance between the layout model and the model.

So now I can go ahead and use the LayoutModel in Layout.cshtml like this:

@model LayoutModel
<!doctype html>
<html>
<head>
<title>@Model.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>

And on a page you can use the generic LayoutModel like this:

@model LayoutModel<Customer>
@{
    var customer = Model.PageModel;
}

<p>Customer name: @customer.Name</p>

From your controller you simply return a model of type LayoutModel:

public ActionResult Page()
{
    return View(new LayoutModel<Customer>(new Customer() { Name = "Test" }, "Title");
}

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to .net

You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to use Bootstrap 4 in ASP.NET Core No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Update .NET web service to use TLS 1.2 EF Core add-migration Build Failed What is the difference between .NET Core and .NET Standard Class Library project types? Visual Studio 2017 - Could not load file or assembly 'System.Runtime, Version=4.1.0.0' or one of its dependencies Nuget connection attempt failed "Unable to load the service index for source" Token based authentication in Web API without any user interface

Examples related to asp.net-mvc

Using Lato fonts in my css (@font-face) Better solution without exluding fields from Binding Vue.js get selected option on @change You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to send json data in POST request using C# VS 2017 Metadata file '.dll could not be found The default XML namespace of the project must be the MSBuild XML namespace How to create roles in ASP.NET Core and assign them to users? The model item passed into the dictionary is of type .. but this dictionary requires a model item of type How to use npm with ASP.NET Core

Examples related to razor

Uncaught SyntaxError: Invalid or unexpected token How to pass a value to razor variable from javascript variable? error CS0103: The name ' ' does not exist in the current context how to set radio button checked in edit mode in MVC razor view @Html.DropDownListFor how to set default value Razor MVC Populating Javascript array with Model Array How to add "required" attribute to mvc razor viewmodel text input editor How to correctly use Html.ActionLink with ASP.NET MVC 4 Areas Multiple radio button groups in MVC 4 Razor How to hide a div element depending on Model value? MVC