web-dev-qa-db-ja.com

Webフォーム内に部分ビューを含める方法

私がプログラミングしているサイトでは、ASP.NET MVCとWebFormsの両方を使用しています。

部分的なビューを持っているので、これをWebフォームに含めたいと思います。部分ビューにはサーバーで処理する必要があるコードがあるため、Response.WriteFileを使用しても機能しません。 javascriptを無効にして動作するはずです。

これどうやってするの?

78
eKek0

MVCソースを見て、これを行う方法を見つけられるかどうかを確認しました。コントローラーコンテキスト、ビュー、ビューデータ、ルーティングデータ、およびhtmlレンダリングメソッドの間には、非常に密接な結合があるようです。

基本的にこれを実現するには、これらの追加要素をすべて作成する必要があります。ビューデータなどの比較的単純なものもありますが、もう少し複雑なものもあります。たとえば、ルーティングデータは現在のWebFormsページが無視されると見なします。

大きな問題はHttpContextのようです。MVCページは(WebFormsのようなHttpContextではなく)HttpContextBaseに依存しており、どちらもIServiceProviderを実装している間、それらは関連していません。 MVCの設計者は、新しいWebベースを使用するためにレガシーWebフォームを変更しないという意図的な決定を下しましたが、ラッパーを提供しました。

これは機能し、WebFormに部分的なビューを追加できます。

public class WebFormController : Controller { }

public static class WebFormMVCUtil
{

    public static void RenderPartial( string partialName, object model )
    {
        //get a wrapper for the legacy WebForm context
        var httpCtx = new HttpContextWrapper( System.Web.HttpContext.Current );

        //create a mock route that points to the empty controller
        var rt = new RouteData();
        rt.Values.Add( "controller", "WebFormController" );

        //create a controller context for the route and http context
        var ctx = new ControllerContext( 
            new RequestContext( httpCtx, rt ), new WebFormController() );

        //find the partial view using the viewengine
        var view = ViewEngines.Engines.FindPartialView( ctx, partialName ).View;

        //create a view context and assign the model
        var vctx = new ViewContext( ctx, view, 
            new ViewDataDictionary { Model = model }, 
            new TempDataDictionary() );

        //render the partial view
        view.Render( vctx, System.Web.HttpContext.Current.Response.Output );
    }

}

次に、WebFormでこれを実行できます。

<% WebFormMVCUtil.RenderPartial( "ViewName", this.GetModel() ); %>
96
Keith

しばらくかかりましたが、素晴らしい解決策を見つけました。 Keithのソリューションは多くの人に有効ですが、特定の状況では最善ではありません。アプリケーションにコントローラーのプロセスを処理させたい場合があるためですビューをレンダリングするために、キースのソリューションは、特定のモデルでビューをレンダリングするだけですここで、通常のプロセスを実行する新しいソリューションを紹介します。

一般的な手順:

  1. ユーティリティクラスを作成する
  2. ダミービューでダミーコントローラーを作成する
  3. aspxまたはmaster pageで、ユーティリティメソッドを呼び出して、コントローラー、ビュー、および必要に応じて(オブジェクトとして)レンダリングするモデルを部分的に渡し、

この例で詳しく調べてみましょう

1)MVCUtilityというクラスを作成し、次のメソッドを作成します。

    //Render a partial view, like Keith's solution
    private static void RenderPartial(string partialViewName, object model)
    {
        HttpContextBase httpContextBase = new HttpContextWrapper(HttpContext.Current);
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Dummy");
        ControllerContext controllerContext = new ControllerContext(new RequestContext(httpContextBase, routeData), new DummyController());
        IView view = FindPartialView(controllerContext, partialViewName);
        ViewContext viewContext = new ViewContext(controllerContext, view, new ViewDataDictionary { Model = model }, new TempDataDictionary(), httpContextBase.Response.Output);
        view.Render(viewContext, httpContextBase.Response.Output);
    }

    //Find the view, if not throw an exception
    private static IView FindPartialView(ControllerContext controllerContext, string partialViewName)
    {
        ViewEngineResult result = ViewEngines.Engines.FindPartialView(controllerContext, partialViewName);
        if (result.View != null)
        {
            return result.View;
        }
        StringBuilder locationsText = new StringBuilder();
        foreach (string location in result.SearchedLocations)
        {
            locationsText.AppendLine();
            locationsText.Append(location);
        }
        throw new InvalidOperationException(String.Format("Partial view {0} not found. Locations Searched: {1}", partialViewName, locationsText));
    }       

    //Here the method that will be called from MasterPage or Aspx
    public static void RenderAction(string controllerName, string actionName, object routeValues)
    {
        RenderPartial("PartialRender", new RenderActionViewModel() { ControllerName = controllerName, ActionName = actionName, RouteValues = routeValues });
    }

パラメーターを渡すためのクラスを作成し、ここでRendeActionViewModelを呼び出します(MvcUtilityクラスの同じファイルで作成できます)

    public class RenderActionViewModel
    {
        public string ControllerName { get; set; }
        public string ActionName { get; set; }
        public object RouteValues { get; set; }
    }

2)DummyControllerという名前のコントローラーを作成します

    //Here the Dummy controller with Dummy view
    public class DummyController : Controller
    {
      public ActionResult PartialRender()
      {
          return PartialView();
      }
    }

次の内容でDummyControllerPartialRender.cshtml(カミソリビュー)と呼ばれるダミービューを作成します。Htmlヘルパーを使用して別のレンダリングアクションを実行することに注意してください。

@model Portal.MVC.MvcUtility.RenderActionViewModel
@{Html.RenderAction(Model.ActionName, Model.ControllerName, Model.RouteValues);}

3)これをMasterPageまたはaspxファイルに配置して、目的のビューを部分的にレンダリングします。 MasterPageまたはaspxページと混在させたい複数のカミソリのビューがある場合、これは素晴らしい答えであることに注意してください。 (Controller HomeのLoginというPartialViewがあると仮定します)。

    <% MyApplication.MvcUtility.RenderAction("Home", "Login", new { }); %>

または、アクションに渡すためのモデルがある場合

    <% MyApplication.MvcUtility.RenderAction("Home", "Login", new { Name="Daniel", Age = 30 }); %>

このソリューションは素晴らしいです、ajaxコールを使用しません、これは遅延レンダリングを引き起こしませんネストされたビューの場合、新しいWebRequestを作成しないため、は表示されませんあなたは新しいセッション、そしてそれはあなたが望むビューのActionResultを取得するためのメソッドを処理します、モデルを渡さずに動作します

ありがとうWebフォーム内でMVC RenderActionを使用

34
Daniel

最も明白な方法は、AJAXを使用することです

このようなもの(jQueryを使用)

<div id="mvcpartial"></div>

<script type="text/javascript">
$(document).load(function () {
    $.ajax(
    {    
        type: "GET",
        url : "urltoyourmvcaction",
        success : function (msg) { $("#mvcpartial").html(msg); }
    });
});
</script>
20
Alexander Taran

これは素晴らしい、ありがとう!

.NET 4でMVC 2を使用していますが、これにはTextWriterがViewContextに渡される必要があるため、以下に示すようにhttpContextWrapper.Response.Outputを渡す必要があります。

    public static void RenderPartial(String partialName, Object model)
    {
        // get a wrapper for the legacy WebForm context
        var httpContextWrapper = new HttpContextWrapper(HttpContext.Current);

        // create a mock route that points to the empty controller
        var routeData = new RouteData();
        routeData.Values.Add(_controller, _webFormController);

        // create a controller context for the route and http context
        var controllerContext = new ControllerContext(new RequestContext(httpContextWrapper, routeData), new WebFormController());

        // find the partial view using the viewengine
        var view = ViewEngines.Engines.FindPartialView(controllerContext, partialName).View as WebFormView;

        // create a view context and assign the model
        var viewContext = new ViewContext(controllerContext, view, new ViewDataDictionary { Model = model }, new TempDataDictionary(), httpContextWrapper.Response.Output);

        // render the partial view
        view.Render(viewContext, httpContextWrapper.Response.Output);
    }
10
Dr. C. Hilarius

これは私のために働いている同様のアプローチです。戦略は、部分ビューを文字列にレンダリングし、それをWebFormページに出力することです。

 public class TemplateHelper
{
    /// <summary>
    /// Render a Partial View (MVC User Control, .ascx) to a string using the given ViewData.
    /// http://www.joeyb.org/blog/2010/01/23/aspnet-mvc-2-render-template-to-string
    /// </summary>
    /// <param name="controlName"></param>
    /// <param name="viewData"></param>
    /// <returns></returns>
    public static string RenderPartialToString(string controlName, object viewData)
    {
        ViewDataDictionary vd = new ViewDataDictionary(viewData);
        ViewPage vp = new ViewPage { ViewData = vd};
        Control control = vp.LoadControl(controlName);

        vp.Controls.Add(control);

        StringBuilder sb = new StringBuilder();
        using (StringWriter sw = new StringWriter(sb))
        {
            using (HtmlTextWriter tw = new HtmlTextWriter(sw))
            {
                vp.RenderControl(tw);
            }
        }

        return sb.ToString();
    }
}

分離コードのページでは、次のことができます

public partial class TestPartial : System.Web.UI.Page
{
    public string NavigationBarContent
    {
        get;
        set;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        NavigationVM oVM = new NavigationVM();

        NavigationBarContent = TemplateHelper.RenderPartialToString("~/Views/Shared/NavigationBar.ascx", oVM);

    }
}

このページでは、レンダリングされたコンテンツにアクセスできます

<%= NavigationBarContent %>

お役に立てば幸いです!

5
aarondcoleman

このソリューションは、異なるアプローチを取ります。これはSystem.Web.UI.UserControlを定義します。これは、Webフォームに配置でき、MVC部分ビューを含むURLからコンテンツを表示するように構成できます。このアプローチは、パラメーター(存在する場合)がURLクエリ文字列を介して提供されるHTMLのAJAX呼び出しに似ています。

まず、2つのファイルでユーザーコントロールを定義します。

/controls/PartialViewControl.ascxファイル

<%@ Control Language="C#" 
AutoEventWireup="true" 
CodeFile="PartialViewControl.ascx.cs" 
Inherits="PartialViewControl" %>

/controls/PartialViewControl.ascx.cs:

public partial class PartialViewControl : System.Web.UI.UserControl {
    [Browsable(true),
    Category("Configutation"),
    Description("Specifies an absolute or relative path to the content to display.")]
    public string contentUrl { get; set; }

    protected override void Render(HtmlTextWriter writer) {
        string requestPath = (contentUrl.StartsWith("http") ? contentUrl : "http://" + Request.Url.DnsSafeHost + Page.ResolveUrl(contentUrl));
        WebRequest request = WebRequest.Create(requestPath);
        WebResponse response = request.GetResponse();
        Stream responseStream = response.GetResponseStream();
        var responseStreamReader = new StreamReader(responseStream);
        var buffer = new char[32768];
        int read;
        while ((read = responseStreamReader.Read(buffer, 0, buffer.Length)) > 0) {
            writer.Write(buffer, 0, read);
        }
    }
}

次に、ユーザーフォームをWebフォームページに追加します。

<%@ Page Language="C#" %>
<%@ Register Src="~/controls/PartialViewControl.ascx" TagPrefix="mcs" TagName="PartialViewControl" %>
<h1>My MVC Partial View</h1>
<p>Below is the content from by MVC partial view (or any other URL).</p>
<mcs:PartialViewControl runat="server" contentUrl="/MyMVCView/"  />
3
Bill Heitstuman

FWIW、既存のWebフォームコードから部分ビューを動的にレンダリングし、特定のコントロールの上部に挿入できるようにする必要がありました。キースの答えにより、部分ビューが<html />タグ。

HttpContext.Current.Response.Outputに直接レンダリングするのではなく、KeithとHilariusからの回答を使用して、html文字列をレンダリングし、LiteralControlとして関連するコントロールに追加しました。

静的ヘルパークラス:

    public static string RenderPartial(string partialName, object model)
    {
        //get a wrapper for the legacy WebForm context
        var httpCtx = new HttpContextWrapper(HttpContext.Current);

        //create a mock route that points to the empty controller
        var rt = new RouteData();
        rt.Values.Add("controller", "WebFormController");

        //create a controller context for the route and http context
        var ctx = new ControllerContext(new RequestContext(httpCtx, rt), new WebFormController());

        //find the partial view using the viewengine
        var view = ViewEngines.Engines.FindPartialView(ctx, partialName).View;

        //create a view context and assign the model
        var vctx = new ViewContext(ctx, view, new ViewDataDictionary { Model = model }, new TempDataDictionary(), new StringWriter());

        // This will render the partial view direct to the output, but be careful as it may end up outside of the <html /> tag
        //view.Render(vctx, HttpContext.Current.Response.Output);

        // Better to render like this and create a literal control to add to the parent
        var html = new StringWriter();
        view.Render(vctx, html);
        return html.GetStringBuilder().ToString();
    }

呼び出しクラスで:

    internal void AddPartialViewToControl(HtmlGenericControl ctrl, int? insertAt = null, object model)
    {
        var lit = new LiteralControl { Text = MvcHelper.RenderPartial("~/Views/Shared/_MySharedView.cshtml", model};
        if (insertAt == null)
        {
            ctrl.Controls.Add(lit);
            return;
        }
        ctrl.Controls.AddAt(insertAt.Value, lit);
    }
0
lukep