web-dev-qa-db-ja.com

部分ビューでMVC 3 ViewBagを変更しても、_Layout.cshtmlに保持されません

RazorビューエンジンでMVC 3を使用しています。パーシャルビュー内のViewBagにいくつかの値を設定し、_Layout.cshtmlでそれらの値を取得したい。たとえば、デフォルトのASP.NET MVC 3プロジェクトをセットアップすると、「/ Views/Shared」フォルダーに_Layout.cshtmlファイルが作成されます。その_Layout.cshtmlでは、ページタイトルは次のように設定されます。

<title>@ViewBag.PageTitle</title>

次に、「/ Views/Home/About.cshtml」ビューで、ViewBagのコンテンツが変更されます。

@{
    ViewBag.Title = "About Us";
}

これは正常に機能します。 Aboutビューがレンダリングされると、ページタイトルは「About Us」になります。だから、今、私はAboutビュー内でPartialビューをレンダリングし、Partialビュー内でViewBag.Titleを変更したいです。 ( "/Views/Shared/SomePartial.cshtml")

@Html.Partial("SomePartial")

この部分ビューには、次のコードがあります。

@{
    ViewBag.Title = "About Us From The Partial View";
}

このコードをデバッグすると、ViewBag.Titleが "About Us"に設定され、Partialビューで "About Us From The Partial View"にリセットされますが、コードが_Layout.cshtmlに達すると、 「会社概要」に戻ります。

これは、ViewBagのコンテンツが部分ビューで変更された場合、それらの変更がメインビュー(About.cshtml)または_Layout.cshtmlに表示されないことを意味しますか?

前もって感謝します!

43
Zoran

私もこの問題を抱えていて、きちんとした明確な解決策を見つけることができませんでした。

私が思いついた解決策は、必要なデータを含む、定義した「PageData」クラスを返すHtml拡張メソッドを実装することでした。

    [ThreadStatic]
    private static ControllerBase pageDataController;
    [ThreadStatic]
    private static PageData pageData;

    public static PageData GetPageData(this HtmlHelper html) {
        ControllerBase controller = html.ViewContext.Controller;
        while (controller.ControllerContext.IsChildAction) {
            controller = controller.ControllerContext.ParentActionViewContext.Controller;
        }
        if (pageDataController == controller) {
            return pageData;
        } else {
            pageDataController = controller;
            pageData = new PageData();
            return pageData;
        }
    }

現在のリクエストのトップレベルのコントローラーを見つけ、同じHTTPリクエスト内でメソッドが呼び出されるたびに同じPageDataオブジェクトを返します。新しいHTTPリクエストで最初に呼び出されたときに、新しいPageDataオブジェクトを作成します。

12
Chris

ViewBagをパーシャルのviewdatadictionaryに渡し、それを引き出し(そしてキャスト)すると、何でもできますし、参照は保持されます。クールな部分は、動的であるため、プロパティを追加して、親ページのビューバッグに表示することもできます。

ページ:

//set the viewbag into the partial's view data
@{Html.RenderPartial("Elaborate", Model, new ViewDataDictionary { {"vb", ViewBag}});}

部分:

@{
   var vb = ((dynamic)ViewData["vb"]);
   vb.TheTitle = "New values";
 }

ページ

@ViewBag.TheTitle = "New value"
31
Tony Basallo

部分ビューは、独自のViewBagを取得します。

ページのViewBagは((WebViewPage) WebPageContext.Current.Page).ViewBagから取得できます

8
SLaks

パーシャルビューでこのトリックを実行して、_Layout.cshtmlのタイトルをオーバーライドできます。

@{
    ViewBag.Title = "About Us From The Partial View";
}

......

<script type="text/javascript">
    document.title = "@ViewBag.Title";
</script>
7
marianotigre

他の人が指摘しているようにレイアウトビュー、および部分自分で取得ViewBag。しかし、私はそれを次のもので動作させることができました:ビューまたはパーシャル。

@{ Html.ViewContext.ViewBag.Title = "Reusable Global Variable"; }

次に、_Layout

@ Html.ViewContext.ViewBag.Title

ViewContextを明示的に使用することにより、ビューは同じViewBagを「再利用」します。

4
Dax70

で@SLaksコードを試す

(((WebViewPage) WebPageContext.Current.Page).ViewBag).PropertyName
1
killebytes

私は次を試してみましたが、うまくいきます:

(親)ビューで...

@Html.Partial("SomePartial", ViewData, null)

注:ViewDatamodel引数として渡されますが、正しいオーバーロードを使用するには、null引数にviewDataを指定する必要があります。 ViewBagを使用できないのは、Html.Partialはダイナミクスが好きではありません。

次に、部分ビューで...

@model ViewDataDictionary
@{
    Model["Title"] = "About us from the partial view";
}

もちろん、実際のモデルにmodel引数を使用する必要がある場合は、より創造的にする必要があります。

1
Oundless

誰かがまだこれに対する解決策を探しているなら、TempDataでそれを行うことができるようです:

TempData["x"] = x;

TempDataは読み取られるまで保持されるため、_Layoutで読み取ることができます。次のリクエストでクリアされるように、すべてを読むように注意する必要があります。

0
chris