如何在 web 表单中包含部分视图

Posted

技术标签:

【中文标题】如何在 web 表单中包含部分视图【英文标题】:How to include a partial view inside a webform 【发布时间】:2010-10-16 17:00:12 【问题描述】:

我正在编程的一些网站同时使用 ASP.NET MVC 和 WebForms。

我有一个部分视图,我想将它包含在一个网络表单中。部分视图有一些必须在服务器中处理的代码,因此使用 Response.WriteFile 不起作用。 它应该在禁用 javascript 的情况下工作。

我该怎么做?

【问题讨论】:

我有同样的问题 - html.RenderPartial 不能在 WebForms 上工作,但应该有办法做到这一点。 【参考方案1】:

花了一些时间,但我找到了一个很好的解决方案。 Keith 的解决方案适用于很多人,但在某些情况下它并不是最好的,因为有时您希望您的应用程序通过控制器的进程 来呈现视图,而 Keith 的解决方案只是用给定的模型渲染视图我在这里提出一个新的解决方案,它将运行正常的过程。

一般步骤:

    创建实用程序类 使用虚拟视图创建虚拟控制器 在您的aspxmaster 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 Class的同一个文件中创建)

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

使用以下内容为DummyController 创建一个名为PartialRender.cshtml(剃刀视图)的虚拟视图,注意它将使用 Html 帮助程序执行另一个渲染操作。

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

3) 现在只需将其放入您的MasterPageaspx 文件中,即可部分渲染您想要的视图。请注意,当您有多个 Razor 视图想要与 MasterPageaspx 页面混合时,这是一个很好的答案。 (假设我们有一个名为 Login 的 PartialView 用于 Controller Home)。

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

或者如果你有一个模型来传递给 Action

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

这个解决方案很棒,不使用ajax调用,不会导致嵌套视图延迟渲染,它不会产生new WebRequest 所以它不会给你带来一个新的会话,它会为你想要的视图处理获取ActionResult的方法它无需通过任何模型即可工作

感谢 Using MVC RenderAction within a Webform

【讨论】:

我尝试了这篇文章中的所有其他解决方案,这个答案是迄今为止最好的。我会建议其他人先尝试这个解决方案。 嗨,丹尼尔。你能帮我么。我遵循了您的解决方案,但发现了一个地方。我在***.com/questions/38241661/… 下提出了它 这绝对是我在 SO 上看到的最好的答案之一。非常感谢。 这对我来说似乎也是一个很好的解决方案,乍一看它似乎确实有效,调用了 dummyController 和视图,并调用了我的控制器和部分视图,但随后请求在 行在我的 aspx 中传递,因此页面的其余部分不会呈现。有没有人遇到过这种行为并知道如何解决?【参考方案2】:

FWIW,我需要能够从现有的 webforms 代码动态呈现部分视图,并将其插入给定控件的顶部。我发现Keith的回答会导致部分视图在&lt;html /&gt;标签之外渲染。

使用 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);
    

【讨论】:

【参考方案3】:

此解决方案采用不同的方法。它定义了一个System.Web.UI.UserControl,它可以放置在任何 Web 表单上,并被配置为显示来自任何 URL 的内容……包括 MVC 部分视图。 这种方法类似于对 HTML 的 AJAX 调用,因为参数(如果有)是通过 URL 查询字符串给出的。

首先,在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/"  />

【讨论】:

我认为这是最好的答案,如果您要多次使用它,您可以重复使用 UserControl,只需更改 contentUrl,我只是建议当前 requestPath 没有得到端口,如果您使用的端口与 80 不同,则会出现错误。 我发现了一个问题,这个方法为请求生成了一个新的Session。所以这就像在同一个地方工作的两个站点。 是的,如果您使用服务器端会话来保存您的应用程序状态,则此解决方案将不起作用。但是,我更喜欢在客户端上维护状态。 乍一看,使用 WebRequest 似乎是一个快速简单的解决方案。但是,根据我的经验,有许多可能导致问题的隐藏问题。如其他答案所示,最好在客户端使用 ViewEngine 或一些 ajax。没有否决票,因为这是一个有效的解决方案,只是我在尝试后不会推荐。 这会将视图代码呈现为字符串,而我猜这个想法是呈现呈现的视图内容@Bill【参考方案4】:

最明显的方式是通过 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>

【讨论】:

在我的回复后添加)-:【参考方案5】:

太好了,谢谢!

我在 .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);
    

【讨论】:

【参考方案6】:

这是一种对我有用的类似方法。策略是将部分视图呈现为字符串,然后在 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 %>

希望有帮助!

【讨论】:

这确实很棒,尤其是当您可以将脚本块放在某个地方时!【参考方案7】:

我查看了 MVC 源代码,看看我是否可以弄清楚如何做到这一点。控制器上下文、视图、视图数据、路由数据和 html 渲染方法之间似乎存在非常紧密的耦合。

基本上,为了实现这一点,您需要创建所有这些额外的元素。其中一些相对简单(例如视图数据),但有些更复杂 - 例如路由数据会考虑忽略当前的 WebForms 页面。

最大的问题似乎是 HttpContext - MVC 页面依赖于 HttpContextBase(而不是像 WebForms 那样的 HttpContext),虽然两者都实现了 IServiceProvider,但它们并不相关。 MVC 的设计者经过深思熟虑,决定不更改旧版 WebForm 以使用新的上下文库,但他们确实提供了一个包装器。

这可以让您向 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() ); %>

【讨论】:

这适用于基本页面请求,但是如果您在容器页面上执行任何回发,view.Render() 会因“视图状态 MAC 验证失败...”异常而崩溃。基思,你能确认一下吗? 我没有收到该视图状态错误 - 但我认为它会发生是您正在呈现的部分视图包括任何 WebForm 控件。此 RenderPartial 方法在渲染时触发 - 在任何视图状态之后。部分视图内的 WebForm 控件将被破坏并超出正常的页面生命周期。 实际上我现在有 - 它似乎发生在某些 WebForms 控件层次结构中,而不是其他的。奇怪的是,错误是从 MVC 渲染方法内部引发的,就好像对 Page 的底层调用一样。 Render 期望进行页面和事件 MAC 验证,这在 MVC 中总是完全错误的。 如果您想知道为什么在 MVC2 及更高版本下无法编译,请参阅 Hilarius 的答案。 也对新的更好的方法感兴趣。我正在使用这种方法在 webforms 母版页中加载部分视图(是的,它有效!)当从母版页调用时,我无法获得控制器上下文,因此必须新建一个。

以上是关于如何在 web 表单中包含部分视图的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET MVC Web 应用程序中包含预编译的视图

我可以在 Web2Py 中包含部分视图,将特定变量传递给它吗?

如何在 django 表单中包含 id 字段?

如何在表单的显示中包含不可编辑的信息? [复制]

如何在表单中包含自定义组件?

如何使用 AFHTTPRequestOperation 在表单字段中包含 JSON