[asp.net] 웹 양식 내부에 부분보기를 포함하는 방법

프로그래밍중인 일부 사이트는 ASP.NET MVC와 WebForms를 모두 사용하고 있습니다.

부분보기가 있고 이것을 웹 양식에 포함하고 싶습니다. 부분보기에는 서버에서 처리해야하는 일부 코드가 있으므로 Response.WriteFile을 사용하면 작동하지 않습니다. 자바 스크립트가 비활성화 된 상태에서 작동합니다.

어떻게 할 수 있습니까?



답변

이 작업을 수행하는 방법을 알아 내기 위해 MVC 소스를 살펴 보았습니다. 컨트롤러 컨텍스트, 뷰, 뷰 데이터, 라우팅 데이터 및 html 렌더링 메서드간에 매우 밀접한 결합이있는 것 같습니다.

기본적으로이를 실현하려면 이러한 모든 추가 요소를 만들어야합니다. 그들 중 일부는 (보기 데이터와 같은) 비교적 단순하지만 일부는 조금 더 복잡합니다. 예를 들어 라우팅 데이터는 현재 WebForms 페이지를 무시하는 것으로 간주합니다.

큰 문제는 HttpContext로 보입니다. MVC 페이지는 WebForms와 같은 HttpContext가 아닌 HttpContextBase에 의존하며 둘 다 IServiceProvider를 구현하지만 서로 관련이 없습니다. MVC의 디자이너는 새로운 컨텍스트 기반을 사용하기 위해 레거시 WebForms를 변경하지 않기로 의도적으로 결정했지만 래퍼를 제공했습니다.

이것은 작동하며 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() ); %>


답변

시간이 좀 걸렸지 만 훌륭한 해결책을 찾았습니다. 키스의 솔루션은 많은 사람들을 위해 작동하지만 때때로 당신은 당신의 응용 프로그램을 원하기 때문에 특정 상황에서 그것은 최선이 아니다 컨트롤러의 절차를 통해 보기를 렌더링, 그리고 키스의 솔루션 그냥 주어진 모델과 뷰를 렌더링 ‘I m 여기서는 정상적인 프로세스를 실행할 새로운 솔루션을 제시합니다.

일반 단계 :

  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();
      }
    }

다음 콘텐츠를 사용하여에 PartialRender.cshtml대해 (면도기보기) 라는 더미보기를 만듭니다 DummyController. Html 도우미를 사용하여 다른 렌더 작업을 수행합니다.

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

3) 이제 원하는 뷰를 부분적으로 렌더링하려면이 파일을 MasterPage또는 aspx파일 에 넣으십시오 . 이는 MasterPage또는 aspx페이지 와 혼합하려는 여러 면도기의보기가있을 때 훌륭한 대답 입니다. (컨트롤러 홈에 로그인이라는 PartialView가 있다고 가정합니다.)

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

또는 Action에 전달하기위한 모델이있는 경우

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

이 솔루션은 중대하다, 아약스 호출을 사용하지 않는 A가 발생하지 않습니다, 렌더링 지연 중첩 된 뷰를 들어, 그것은 새로운 WebRequest 클래스가되지 않습니다 그것 때문에 당신에게 새로운 세션을 가져 오지 않습니다 , 그리고 그것을 검색하는 방법을 처리합니다 원하는 뷰에 대한 ActionResult모델을 전달하지 않고 작동합니다.

Webform 내에서 MVC RenderAction 사용 덕분에


답변

가장 확실한 방법은 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>


답변

고마워요!

TextWriter가 ViewContext에 전달되어야하는 .NET 4에서 MVC 2를 사용하고 있으므로 아래와 같이 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);
    }


답변

여기 저에게 효과가있는 유사한 접근 방식이 있습니다. 전략은 부분보기를 문자열로 렌더링 한 다음 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 %>

도움이 되었기를 바랍니다.


답변

이 솔루션은 다른 접근 방식을 취합니다. System.Web.UI.UserControl웹 양식에 배치 할 수 있고 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);
        }
    }
}

그런 다음 웹 양식 페이지에 사용자 정의 컨트롤을 추가합니다.

<%@ 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/"  />


답변

FWIW, 기존 웹 양식 코드에서 부분보기를 동적으로 렌더링하고 지정된 컨트롤의 맨 위에 삽입 할 수 있어야했습니다. Keith의 답변으로 인해 부분보기가 외부에서 렌더링 될 수 있음을 발견했습니다.<html /> 태그 .

영감을 얻기 위해 Keith와 Hilarius의 답변을 사용하여 HttpContext.Current.Response.Output에 직접 렌더링하는 대신 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);
    }