开发者

How can I write dynamic data to page layout in MVC 3 Razor?

I have MVC 3 C# project 开发者_StackOverflow社区with Razor engine. What are the ways and, I guess, the best practices to write dynamic data to the _Layout.cshtml?

For example, maybe I'd like to display user's name in the upper right corner of my website, and that name is coming from Session, DB, or whatever based on what user is logged in.

UPDATE: I'm also looking for a good practice on rendering certain data into the element of the Layout. For example, if I need to render a specific CSS file depending on the logged-in user's credentials.

(For the example above, I thought of using Url Helpers.)


The default internet application created by visual studio use _LogOnPartial.cshtml to do exactly this.

The user Name value is set in the LogOn action of the HomeController

Code for _LogOnPartial.cshtml

@if(Request.IsAuthenticated) {
    <text>Welcome <strong>@User.Identity.Name</strong>!
    [ @Html.ActionLink("Log Off", "LogOff", "Account") ]</text>
}
else {
    @:[ @Html.ActionLink("Log On", "LogOn", "Account") ]
}

User.Identity is part of the aspnet Membership provider.

Code for the _Layout.cshtml

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <header>
            <div id="title">
                <h1>Test</h1>
            </div>
            <div id="logindisplay">
                @Html.Partial("_LogOnPartial")
            </div>
            <nav>
                <ul id="menu">
                </ul>
            </nav>
        </header>
        <section id="main">
            @RenderBody()
        </section>
        <footer>
        </footer>
    </div>
</body>
</html>

Code for the AccountController Logon Action

[HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                if (Membership.ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                    if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {
                    ModelState.AddModelError("", "The user name or password provided is incorrect.");
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

Code for ApplicationViewPage class

public abstract class ApplicationViewPage<T> : WebViewPage<T>
    {
        protected override void InitializePage()
        {
            SetViewBagDefaultProperties();
            base.InitializePage();
        }

        private void SetViewBagDefaultProperties()
        {
            ViewBag.LayoutModel = new LayoutModel(Request.ServerVariables["SERVER_NAME"]);
        }

    }

The above code allow me to have a ViewBag.LayoutModel that hold an instance of my LayoutModel class in every page.

Here is a code for my LayoutModel class

public class LayoutModel
    {
        public string LayoutFile { get; set; }
        public string IpsTop { get; set; }
        public string IpsBottom { get; set; }
        public string ProfileTop { get; set; }
        public string ProfileBottom { get; set; }

        public LayoutModel(string hostname)
        {
            switch (hostname.ToLower())
            {
                default:

                    LayoutFile = "~/Views/Shared/_BnlLayout.cshtml";
                    IpsBottom = "~/Template/_BnlIpsBottom.cshtml";
                    IpsTop = "~/Template/_BnlTop.cshtml";
                    ProfileTop = "~/Template/_BnlProfileTop.cshtml";
                    break;

                case "something.com":
                    LayoutFile = "~/Views/Shared/_Layout.cshtml";
                    IpsBottom = "~/Template/_somethingBottom.cshtml";
                    IpsTop = "~/Template/_somethingTop.cshtml";
                    ProfileTop = "~/Template/_somethingProfileTop.cshtml";
                    break;
            }
        }
    }

Here is the code to the View

@{
    ViewBag.Title = "PageTitle";
    Layout = @ViewBag.LayoutModel.LayoutFile; 
}
@using (Html.BeginForm())
{
    <span class="error">@ViewBag.ErrorMessage</span>
    <input type="hidden" name="Referrer" id="Referrer" value="@ViewBag.Referrer" />
    html stuff here       
}

Refer to the following question for more detail. Make sure you modify your web.config as described there: How to set ViewBag properties for all Views without using a base class for Controllers?


In addition to atbebtg's answer, to render stuff into the head, you want to leverage Razor's section support. Sections are named fragments of templated HTML that can be defined in views and rendered in the layout, where the layout sees fit. In the layout, you call @RenderSection("wellKnownSectionName") and in the view that uses the layout, you declare @section wellKnownSectionName { <link rel="stylesheet" href="@UserStylesheetUrl" /><script type="text/javascript" src="@UserScriptUrl"> }. Usually you want to describe the intention of the section in its name, such as "documentHead".

Update: If you're rendering the same templated HTML on every view, it would instead go into the layout. (Since your layout includes the HEAD and the BODY tags, you can just add the appropriate code to the HEAD tag their.) You just need to make sure you pass the information necessary for the layout from the controller via the ViewBag/View.Model/ViewData. So your layout would include this:

<head>
    <link rel="stylesheet" href="/css/@ViewBag.UserName/.css"/>
</head>

and your controller would include the logic to populate ViewBag.UserName:

ViewBag.UserName = Session["UserName"];

(Ideally you would use a strongly-typed view model and I would recommend you abstain from using Session for anything, since its benefits are small compared to alternatives and comes at a big cost to architecture... instead I would simply recommend storing some encrypted cookie on the browser that contains a username or something, which you can use on each page load to retrieve the user object from cache/db/service.)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜