ASP.NET MVC: RenderBody, RenderSection, RenderPage, RenderPartial

Dzisiaj kolejny wpis o podstawach ASP.NET MVC. Tym razem chciałbym pokazać czym różnią się metody RenderBody, RenderSection, RenderPage oraz RenderPartial.

Zacznijmy od najbardziej znanej metody, RenderBody. Korzystamy z niej w przypadku zdefiniowanego Layout’u. Tworząc nowy, domyślny projekt automatycznie zostanie dodany w folderze shared plik _Layout.cshtml. Definiuje on domyślny szablon czyli zwartość, która jest powtarzana na każdej podstronie. Zamiast duplikować cześć wspólną (menu, stopka itp.) na każdej podstronie, możemy ją zdefiniować tylko raz w layout’cie. Przykład:

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <div id="header">
            <div id="title">
                <h1>My MVC Application</h1>
            </div>
            <div id="logindisplay">
                @Html.Partial("_LogOnPartial")
            </div>
            <div id="menucontainer">
                <ul id="menu">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                </ul>
            </div>
        </div>
        <div id="main">
            @RenderBody()
        </div>
        <div id="footer">
        </div>
    </div>
</body>
</html>

Metoda RenderBody służy do wstrzyknięcia konkretnej treści. Layout definiuje cześć wspólną, ale potem w podstronach chcemy wstawić konkretną treść. W miejsce RenderBody, zostanie zatem umieszczona konkretna treść, specyficzna dla danej strony. Następnie aby skorzystać z layoutu, należy na podstronie albo w pliku ViewStart umieścić następujący kod:

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Teraz widoki powinny definiować wyłącznie treść specyficzną dla danej strony, która zostanie wstrzyknięta w miejsce RenderBody.

RenderPage ma dość podobne zastosowanie. Służy do wyrenderowania konkretnej strony. Jako parametr przekazujemy nazwę strony, którą chcemy wygenerować oraz opcjonalnie parametry wejściowe.

Stwórzmy zatem jakiś widok w folderze Shared:

@{
    ViewBag.Title = "SamplePage";
}

<h2>SamplePage</h2>
Jakas tresc

Następnie na dowolnej podstronie wyrenderujmy SamplePage:

@{
    ViewBag.Title = "About";
}

<h2>About</h2>

@RenderPage("~/Views/Shared/SamplePage.cshtml")

Finalna treść będzie stanowić połączenie szablonu (layout), strony about oraz wyrenderowanej w niej SamplePage.cshtml. Nic nie szkodzi na przeszkodzie, aby wywołać kilka razy RenderPage na tej samej stronie. Po prostu, gdy mamy treść powtarzającą się na kilku stronach, możemy skorzystać z RenderPage.

Przekazanie parametru do RenderPage jest bardzo łatwe:

@RenderPage("~/Views/Shared/SamplePage.cshtml",new{Par1="Test"});

Następnie (SamplePage.cshtml):

@{
    ViewBag.Title = "SamplePage";
}

<h2>SamplePage</h2>
Jakas tresc


@Page.Par1

Alternatywnie można skorzystać z @PageData["Par1"]. Oczywiście w praktyce przekazuje się dane z Modelu, a nie ręcznie zdefiniowane w html.

Kolejna metoda, RenderSection jest znów ściśle powiązana z szablonem (_Layout). Ponadto, możemy definiować dowolną ilość sekcji:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - My ASP.NET MVC Application</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        <h1>Glowna tresc</h1>
        @RenderBody();
        <h2>Sekcja I</h2>
        @RenderSection("Section1",required:false);
        <h2>Sekcja II</h2>
        @RenderSection("Section2",required:false);
    </body>
</html>

Pierwszy parametr metody to nazwa sekcji, drugi z kolei to flaga oznaczająca czy sekcja zawsze musi zostać użyta. Następnie na dowolnej podstronie możemy wstawić treść w poszczególne sekcje:

@{
    ViewBag.Title = "About";
}

<h2>About</h2>

Glowna tresc

@section Section1
{
    Jakas tresc dla sekcji I
}

Jeśli ustawimy, że sekcja jest wymagana a nie wypełnimy ją treścią wtedy wystąpi błąd podczas generowania strony:

Section not defined: "Section2". 

Na zakończenie warto również przyjrzeć się RenderPartial. Metoda, analogicznie jak RenderPage służy do wyrenderowania strony, przekazanej jako parametr wejściowy. Różnica polega na tym, że w przypadku RenderPartial podajemy po prostu nazwę widoku, a nie pełną ścieżkę:

@{
    ViewBag.Title = "About";
}

<h2>About</h2>

Glowna tresc

@{ Html.RenderPartial("SamplePage"); }

@RenderPage("~/Views/Shared/SamplePage.cshtml")

Efekt docelowy jest ten sam – mechanizm za to jest trochę inny, szczególnie w przypadku wyszukiwania. RenderPartial podajemy tylko nazwę, zatem widok może znajdować się w Shared lub w folderze dedykowanym dla danego kontrolera.