Miguel Moura
Miguel Moura

Reputation: 39374

Base View Page in ASP.NET MVC 6

On ASP.NET MVC 5 I used a base ViewPage with a few properties:

public String PageTitle { get; set; }
public String PageDescription { get; set; }
public String[] BodyCssClasses { get; set; }

Then on each view I would have:

@{
  PageTitle = "Title ..."
  PageDescription" = "Description ..."
  BodyCssClasses = new String[] { "main", "home" }
}

On the master page I would simply use something like:

<title>@Title</title>

With this approach I was able to use Strong Typed for page properties ...

Is it possible to use a Base View Page in ASP.NET MVC 6?

Since there is no Web.Config how could this be done?

Any suggestions for better options to define page head info is welcome.

UPDATE

I followed the suggested and I am using:

public abstract class ViewPageBase<TModel> : RazorPage<TModel> {
  public String Title { get; set; }
} // ViewPageBase

Then on _ViewImports I have:

@inherits ViewPageBase<TModel>

On _Layout.cshtml I have:

<title>@Title</title>

And finally on a view which uses that layout I have:

@{
  Title = "Page Title";
  Layout = "_Layout";
}

Everything compiles and runs but the page title is always empty.

Does anyone has any idea why?

Upvotes: 9

Views: 4005

Answers (3)

Chris Nielsen
Chris Nielsen

Reputation: 14748

The technique you have described in your update will technically work.

Unfortunately, as @Peter notes, you get one instance of your custom ViewPage for the view, and a different instance for the layout. Because you are using two different instances, you cannot set a property on the view and expect to access that property on the layout. That's the answer to your updated question:

Everything compiles and runs but the page title is always empty. Does anyone has any idea why?

@Peter has offered a work around using the ViewBag. I'd like to offer an additional workaround using dependency injection.

For instance, consider placing your custom properties in their own class, with its own interface:

public interface IPageMetaData {
    string PageTitle { get; set; }
    string PageDescription { get; set; }
    string[] BodyCssClasses { get; set; }
}

public class PageMetaData : IPageMetaData {
    public string PageTitle { get; set; }
    public string PageDescription { get; set; }
    public string[] BodyCssClasses { get; set; }
}

Now, register this with the .Net Core dependency injection framework. Use the Scoped lifecycle, so only one instance will be created per-web request. In a vanilla project, this takes place in Startup.cs, in the ConfigureServices method:

public void ConfigureServices(IServiceCollection services) {
    // Add framework services.
    services.AddMvc();
    services.AddScoped<IPageMetaData, PageMetaData>();
}

Finally, update your _ViewImports.cshtml file to inject this object into your views:

@inject Your.Namespace.Goes.Here.IPageMetaData MetaData;

Now your views will have a ambient MetaData property that will correspond to a per-web request instance of the PageMetaData class. You can access it from both views and from the layout:

@{
    // In a view
    MetaData.PageTitle = "Set a page title";
}

@* In a layout *@
<title>@MetaData.PageTitle</title>

Upvotes: 6

Peter
Peter

Reputation: 12711

You'll probably want to make your base view page inherit from RazorPage.

public abstract class ViewPageBase<TModel> : RazorPage<TModel>
{
}

Then you should be able to configure all your pages to inherit from this in the _ViewImports.cshtml file.

@inherits ViewPageBase<TModel>

UPDATE

Not sure if this is the best approach, but I wonder if you could use the common ViewBag to share data between your View and the Layout.

Back the properties in your base page class with the ViewBag:

public abstract class ViewPageBase<TModel> : RazorPage<TModel>
{
    public string Title
    {
        get { return ViewBag.Title; }
        set { ViewBag.Title = value; }
    }
}

Set the property in your view:

@{
    Title = "Home Page"; 
}

Use the property in _Layout.cshtml:

<title>@Title</title>

Upvotes: 3

N. Taylor Mullen
N. Taylor Mullen

Reputation: 18301

Sounds like you're looking for the @inherits Razor directive.

For instance:

@inherits MyBaseViewPage

Upvotes: 1

Related Questions