Как включить частичный вид внутри веб-формы

Некоторые сайты, которые я программирую, используют как ASP.NET MVC, так и WebForms.

У меня есть частичный вид, и я хочу включить его в веб-форму. Частичный вид имеет некоторый код, который должен быть обработан на сервере, поэтому использование Response.WriteFile не работает. Он должен работать с отключенным javascript.

Как я могу это сделать?

Ответ 1

Я посмотрел источник MVC, чтобы узнать, могу ли я понять, как это сделать. По-видимому, существует очень тесная связь между контекстом контроллера, представлениями, данными просмотра, данными маршрутизации и методами рендеринга html.

В принципе, чтобы это произошло, вам нужно создать все эти дополнительные элементы. Некоторые из них относительно просты (например, данные представления), но некоторые из них немного сложнее - например, данные маршрутизации будут учитывать игнорирование текущей страницы WebForms.

Большая проблема заключается в том, что страницы HttpContext - MVC основаны на HttpContextBase (а не на HttpContext, например, на WebForms), и хотя оба они реализуют 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() ); %>

Ответ 2

Это заняло некоторое время, но я нашел отличное решение. Решение Keith работает для многих людей, но в определенных ситуациях оно не самое лучшее, потому что иногда вы хотите, чтобы ваше приложение прошло процесс контроллера для визуализации представления, а решение Keith просто отображает представление с заданной моделью я ' Я представляю здесь новое решение, которое будет запускать обычный процесс.

Основные шаги:

  1. Создать класс Utility
  2. Создать фиктивный контроллер с фиктивным видом
  3. На вашей aspx или master page вызовите служебный метод для частичного рендеринга, передавая Контроллер, просмотрите и, если вам нужно, модель для рендеринга (как объект),

Давайте проверим это внимательно в этом примере

1) Создайте класс с именем MVCUtility и создайте следующие методы:

    //Render a partial view, like Keith 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();
      }
    }

Создайте Dummy-представление с именем PartialRender.cshtml (PartialRender.cshtml view) для DummyController со следующим содержимым, обратите внимание, что он выполнит еще одно действие рендеринга с использованием вспомогательного средства Html.

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

3) Теперь просто поместите это в файл MasterPage или aspx, чтобы частично отобразить желаемое представление. Обратите внимание, что это отличный ответ, когда у вас есть несколько видов бритвы, которые вы хотите смешать со своими страницами MasterPage или aspx. (Предположим, у нас есть PartialView, называемый Login for Controller Home).

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

или если у вас есть модель для перехода в действие

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

Это отличное решение, оно не использует ajax-вызов, который не вызовет отложенного рендеринга для вложенных представлений, не создает новый WebRequest, поэтому он не принесет вам новый сеанс и обработает метод для извлечения. ActionResult для нужного вам представления, он работает без прохождения любой модели

Благодаря использованию MVC RenderAction в веб-форме

Ответ 3

Самый очевидный способ - через 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>

Ответ 4

Это здорово, спасибо!

Я использую MVC 2 на .NET 4, который требует, чтобы 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);
    }

Ответ 5

Вот аналогичный подход, который работал у меня. Стратегия состоит в том, чтобы отобразить частичный вид строки, а затем выводить ее на страницу 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();
    }
}

На странице codebehind вы можете сделать

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 %>

Надеюсь, что это поможет!

Ответ 6

Это решение использует другой подход. Он определяет System.Web.UI.UserControl, который может быть размещен на любой веб-форме и настроен для отображения содержимого с любого URL-адреса... включая частичный просмотр MVC. Этот подход похож на вызов AJAX для HTML в том, что параметры (если они есть) предоставляются через строку запроса URL.

Сначала определите пользовательский элемент управления в двух файлах:

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

Ответ 7

Кстати, мне нужно было иметь возможность динамически визуализировать частичное представление из существующего кода веб-форм и вставить его в верхнюю часть данного элемента управления. Я обнаружил, что ответ Кейта может вызвать частичное представление за пределами <html/>.

Используя ответы Кейта и Хилариуса для вдохновения, вместо того, чтобы отрисовывать напрямую 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);
    }