29

I have a partial "Sidebar" added to the master page (Layout) and inside this partial I'm using:

@RenderSection("SearchList", required: false)

On one of the views that uses the master page I'm doing:

@section SearchList {
    // bunch of html
}

but it's giving me the error:

The file "~/Views/Shared/_SideBar.cshtml" cannot be requested directly because it calls the "IsSectionDefined" method.

What's wrong here?

raklos
  • 28,027
  • 60
  • 183
  • 301

4 Answers4

23

What you are trying to do is not currently supported in Razor. Sections only work between the view page and its immediate layout page.

marcind
  • 52,944
  • 13
  • 125
  • 111
  • Any workaround? I have a Layout->Page->Partial. I need scripts/layouts to be referenced and loaded in the head when that partial is there. Any non-stupid way to do this?? What's the point of this restriction anyway? – Shimmy Weitzhandler Nov 26 '12 at 01:33
  • @Shimmy you could try putting some sort of datastructure into ViewData that specifies which things should be referenced by the Layout page. – marcind Nov 26 '12 at 23:50
  • Thanks. I'm just reading about [Cassette](http://getcassette.net/) now, which looks like a decent solution. Thank you. – Shimmy Weitzhandler Nov 27 '12 at 00:03
16

When creating your layout view you might want to put some pieces seperatly into partial views.

You might also encounter the need to render sections that need to be placed into the markup of one of these partial views. However, since partial views do not support RenderSection logic you'll have to work around this.

You can render sections in a partial views by passing the RenderSection result from your Layout page as the model of the partial view. You can do this by putting this line of code in your _Layout.cshtml.

_Layout.cshtml

@{ Html.RenderPartial("_YourPartial", RenderSection("ContextMenu", false));}

Then in _YourPartial.cshtml you can render the section passed along as the model in the Html.RenderPartial call on the _Layout view. You check if the model is null just in case your section is not required.

_YourPartial.cshtml

@model HelperResult
@if (Model != null)
{
    @Model
}
Bosken85
  • 617
  • 6
  • 8
  • 1
    What? Interesting, but you should explain it a bit further as this is a bit of a hack. – ATL_DEV Oct 30 '14 at 15:29
  • Yes. Thanks. Still a bit hacky, but does the job I guess. upvoted. – ATL_DEV Nov 11 '14 at 00:40
  • This doesn't work if the section is not defined - the Page's model is passed through to the partial, and an exception is thrown: "The model item passed into the dictionary is of type 'xx.yyModel', but this dictionary requires a model item of type 'System.Web.WebPages.HelperResult'. – Steven Sproat Sep 18 '15 at 09:35
5

It is possible to solve this with razor helpers. It's kinda elegantly-hacky™ but it did the job for me.

So in a parent view you define a helper:

@helper HtmlYouWantRenderedInAPartialView()
{
    <blink>Attention!</blink>
}

Then when you render partial, you pass this helper to it

@Html.Partial("somePartial", new ViewDataDictionary { { "OptionalSection1", (Func<HelperResult>)(HtmlYouWantRenderedInAPartialView) } })

Then inside a partial view you call this helper like so

<div>@ViewData.RenderHelper("OptionalSection1")</div>

Finally you need to have this extension method to simplify the "calling" part

public static HelperResult RenderHelper(this ViewDataDictionary<dynamic> viewDataDictionary, string helperName)
{
    Func<HelperResult> helper = viewDataDictionary[helperName] as Func<HelperResult>;
    if (helper != null)
    {
        return helper();
    }

    return null;
}

So the whole point is to pass a delegate of this helper and then when the child view calls it, the contents get rendered where you want them.

The end result of a child view would look like this

<div><blink>Attention!</blink></div>
mdonatas
  • 1,770
  • 2
  • 15
  • 15
0

While Bosken85's answer is close, it doesn't quite work when the section is not defined in your view. The if statement isn't used, (an exception is thrown first.) You need to overload the RenderPartial including 3 parameters so that the non-existent RenderSection isn't ignored, causing the wrong model to be passed to the partial layout.

In _Layout.cshtml (your overall layout) you use:

@{Html.RenderPartial("_Partial", RenderSection("sectionNameGoesHere", false), new ViewDataDictionary());}

In _Partial.cshtml (your partial layout that wants to define a RenderSection) you use:

@model HelperResult
@Model

In Index.cshtml (a layout that uses _Layout.cshtml for it's layout and you want to put some content into the partial layout's section) you use:

@section sectionNameGoesHere{stuff you want in the section goes here}
Brian Heward
  • 524
  • 4
  • 14