1

I have an MVC Masterpage.

Within the body, I have a placeholder for a title that all the views using this masterpage populate:

<asp:ContentPlaceHolder ID="TitleContent" runat="server" />

But as well as this title being used how it is, I want the exact same text to go in the title section of the head tag.

Of course, I could use another placeholder and make every single view specify the same content twice, but it would be better if some code could magically copy the literal text out of the placeholder and put it into the head title tag as well.

Obviously Javascriptis no use, because google wont process it to the page title. So I need to do this serverside.

tereško
  • 58,060
  • 25
  • 98
  • 150
James
  • 233
  • 3
  • 14

3 Answers3

1

In general, you would bind these values in the view to data either on a model or perhaps in a ViewBag (or ViewData for older versions of MVC) or something of that nature. So in your Layout (Master Page? Must be an older version of MVC?) you might have a couple of references to a ViewBag (or ViewData) value. Something like this:

<html>
    <head>
        <title><%= ViewBag.PageTitle %></title>
    </head>
    <body>
        <div id="somePageTitle"><%= ViewBag.PageTitle %></div>
    </body>
</html>

or:

<html>
    <head>
        <title><%= ViewData["PageTitle"] %></title>
    </head>
    <body>
        <div id="somePageTitle"><%= ViewData["PageTitle"] %></div>
    </body>
</html>

Then in your controller you would set that value as needed:

ViewBag.PageTitle = "This is a page title";

or:

ViewData["PageTitle"] = "This is a page title";

That would bind that value to both locations in the view. It's more common to use ViewBag (or other constructs, like ViewData and TempData, often depending on the MVC version being used) for elements in the layout (Master Page) and to use view models for elements in the specific view.

David
  • 208,112
  • 36
  • 198
  • 279
  • WebForms is a view engine that is available to MVC. It is what we had prior to Razor – Nick Bork Jul 18 '13 at 21:25
  • yeah, mvc has a @razor version and a asp.net version in which most controls still work. I find it ugly and hacky, but it's a good intermediate for migrating from classic asp.net > mvc asp.net > mvc razor – Robert Hoffmann Jul 18 '13 at 21:27
  • Thanks, I did think about this route using ViewData... I am using an MVC masterpage and yes the asp placeholder works. I just hate doing lots of programmatic stuff for what is essentially view-related. The text of a page should go in the view not the controller. – James Jul 18 '13 at 21:27
  • @NickBork: There was an ASPX view engine, which supported the classic `<% %>` syntax, sure. But the view engine itself wasn't called WebForms, that's an entirely different paradigm than MVC. And I've never seen server-side controls like that used in MVC. If they get populated from the controller code then that *completely* undermines the structure of MVC and the separation of concerns it tries to achieve. So even if it does work, it's not using the framework correctly and isn't recommended. – David Jul 18 '13 at 21:28
  • WebFormViewEngine, System.Web.Mvc.WebFormViewEngine http://stackoverflow.com/questions/1451319/asp-net-mvc-view-engine-comparison The Master pages used <asp:contentplaceholder id="TitleContent" runat="server"></asp:contentplaceholder> and the Views used MPage name – Nick Bork Jul 18 '13 at 21:30
  • @NickBork: Interesting. I'd never heard it referred to as the Web Form View Engine. But referring to it as "WebForms" is still definitely inviting confusion. And the point still remains that the old WebForms server-side controls shouldn't be used in this context. He *should* be binding values from models or other view-friendly constructs. – David Jul 18 '13 at 21:34
  • OK, so based on the fact I am using MVC 3 with the <% syntax (not Razor), what do you recommend instead of placeholders? – James Jul 18 '13 at 21:35
  • @David pure server-side controls won't work, but all the others do: input, button, literal, placeholder, etc ..you can also manipulate them from within your view (like inline c# before) – Robert Hoffmann Jul 18 '13 at 21:37
  • @James we already replied: use ViewBag (though i strongly suggest you switch to razor if your project is not a migration project, or not very advanced yet ..you won't regret it) – Robert Hoffmann Jul 18 '13 at 21:38
  • @James: Same concept as in the answer, but with the `<% %>` syntax instead of the `@` syntax. – David Jul 18 '13 at 21:39
  • @James: I've updated the code in the answer to reflect the syntax difference. (And fixed a typo in the controller code.) – David Jul 18 '13 at 21:40
  • 1
    @David I don't think you understand, he isn't using "server-side controls". The markup he posted is how you define what Razor terms "sections". He has created a placeholder so that in his View he can set the value. That *IS* how the MVC WebFormsViewEngine (based of WebForms) works. You're giving misleading information. I understand that you may not have used this View Engine before but the information you're providing is incorrect. ViewBag can be used in MVC3 and later. ViewData is avaliable in previous versions of MVC. This method can be used to set the title. – Nick Bork Jul 18 '13 at 21:45
  • @NickBork duh, i was thinking like David. You are right `` is the equivalent of @section TitleContent { } / @RenderSection("TitleContent") in @razor. But David's answer is still correct: ViewBag – Robert Hoffmann Jul 18 '13 at 21:47
  • @NickBork: Ah, I see now. I was definitely mistaken on that. I'll re-configure the answer accordingly. Thanks! – David Jul 18 '13 at 21:50
  • @David your kinda repeating yourself in the code ViewData[""] is pre .net4, VieBag is the new default and .net4+ ..well actually both still exists today: ViewBag is dynamic, and ViewData gives you an array like access, but both point to the same object – Robert Hoffmann Jul 18 '13 at 21:55
  • @Robert: Indeed, it depends on the version being used. I recommend `ViewBag` but if the OP is on an older version of MVC and can't use it then he can default to `ViewData`. The repetition is to demonstrate both options. – David Jul 18 '13 at 21:56
1

First off David's answer is right

Actual implementation would be whatever.master

<!DOCTYPE html>
<html>
    <head>
        <title><%= ViewData["Title"] %></title>
    </head>
    <body>
        <asp:ContentPlaceHolder ID="TitleContent" runat="server"/>
    </body>
</html>

In your View

<asp:Content ContentPlaceHolderID="TitleContent" runat="server">
    <%= ViewData["Title"] %> 
</asp:Content>

Change it in the controller

public ActionResult Index()
{
    ViewData["Title"] = "My new title";
    return View();
}

Or set it in the View itself

<script runat="server">
    ViewData["Title"] = "My new title";
</script>

<asp:Content ContentPlaceHolderID="TitleContent" runat="server">
    <%= ViewData["Title"] %> 
</asp:Content>

Since these are variable, use them anywhere in your page you like

// These are the same
ViewData["Title"] = "My new title";
ViewBag.Title     = "My new title";

// These are the same
<%= ViewData["Title"] %>
<%= ViewBag.Title %>

public Dictionary<string, object> ViewData
public dynamic ViewBag // .NET 4.0 +
Robert Hoffmann
  • 2,366
  • 19
  • 29
0

Thanks for all your help.

I wanted to set the data in the view because I have different controllers using the same view, and also some controllers returning different views depending on the logic, so it made sense to define the page title on the view where it is a known entity, especially since it is usually not model data and is thus always a constant text for that page.

One complication, is that if the view was to be set as a dynamic piece of data taken from the model, that hadn't been calculated yet (such as a deferred execution iqueryable), then the masterpage never realizes the value unless you populate it in the view at a higher point in the page.

In other words, the view has to set the viewdata in script in a content placeholder ABOVE where it will be in the masterpage. You cannot set the viewdata[] property in the body content if the masterpage uses it in the head section.

This works:

<head>
    <asp:Placeholder ID="HeadContent" runat="server"/>
    <title><%: ViewData["PageTitle"] %></title>
</head>
<body>
    <%: ViewData["PageTitle"] %>
</body>

Then in the view you can set the title:

<asp:Content ContentPlaceHolderID="HeadContent" runat="server">
    <% ViewData["PageTitle"] = Model.SomeValueThatHasDefferedExecution; %>
</asp:Content>
James
  • 233
  • 3
  • 14