.NET MVC 3 Programmatically set layout

2019-04-08 11:34发布

问题:

In a .NET Razor Web Application i'm trying to programmatically set the Layout. I can not use _ViewStart.cshtml and don't wont to set the @{ Layout = "..." } on every page. This is what I have come up with:

A base WebViewPage class:

public abstract class SitePage<T> : System.Web.Mvc.WebViewPage<T>
{
    private object _layout;

    public new dynamic Layout { get { return _layout; } }

    public override void InitHelpers()
    {
        base.InitHelpers();
        _layout = "~/Themes/" + Settings.Theme + "/Views/_Layout.cshtml";
    }
}

And in the application web.config I specify all view to use this base page. But the Layout is never used it seems. What could be wrong here?

回答1:

The WebViewPage class inherits from WebPageBase that has a property named Layout like:

public override string Layout { get; set; }

You can override the Layout property, or change your _layout logic to achieve your purpose. For example:

public abstract class SitePage<T> : System.Web.Mvc.WebViewPage<T> {

    // set this modifier as protected, to make it accessible from view-pages
    protected string _layout{
        get {
            return base.Layout;
        }
        set {
            base.Layout = value;
        }
    }

    public override void InitHelpers() {
        base.InitHelpers();
        _layout = "~/Themes/" + Settings.Theme + "/Views/_Layout.cshtml";
    }
}

and/or in a view-page, you can set it too:

@{
    _layout = "_Your_Special_Layout.cshtml";
}

UPDATE: using a flag to avoid stack-over-flow in assigning _layout more that once:

public abstract class SitePage<T> : System.Web.Mvc.WebViewPage<T> {

    public bool LayoutAssigned {
        get {
            return (ViewBag.LayoutAssigned == true);
        }
        set {
            ViewBag.LayoutAssigned = value;
        }
    }

    // set this modifier as protected, to make it accessible from view-pages
    protected string _layout{
        get {
            return base.Layout;
        }
        set {
            base.Layout = value;
        }
    }

    public override void InitHelpers() {
        base.InitHelpers();
        if(!LayoutAssigned) {
            _layout = "~/Themes/" + Settings.Theme + "/Views/_Layout.cshtml";
            LayoutAssigned = true;
        }
    }
}


回答2:

I tried to achieve the same just now by implementing a custom WebViewPage, however changing WebViewPage.Layout within my custom class didn't have any effect (as you have also discovered).

Eventually I ended up changing my _ViewStart.cshtml to have this code:

@{
    this.Layout = this.Request.QueryString["print"] == "1" 
        ? "~/Views/Layout/_Print.cshtml"
        : "~/Views/Layout/_Layout.cshtml";
}

It might not be implemented how you wanted it, but it certainly does keep things dry and that is the main point.