2009-03-31 25 views
69

Một số trang web tôi đang lập trình đang sử dụng cả ASP.NET MVC và WebForms.Cách bao gồm một phần xem bên trong biểu mẫu web

Tôi có một chế độ xem một phần và tôi muốn bao gồm nội dung này bên trong biểu mẫu web. Chế độ xem một phần có một số mã phải được xử lý trong máy chủ, vì vậy việc sử dụng Response.WriteFile không hoạt động. Nó sẽ hoạt động với javascript bị vô hiệu hóa.

Tôi làm cách nào để thực hiện việc này?

+0

Tôi có cùng một vấn đề - Html.RenderPartial không thể làm việc trên WebForms, nhưng vẫn còn có một cách để làm điều này. – Keith

Trả lời

90

tôi đã có một cái nhìn tại các nguồn MVC để xem nếu tôi có thể tìm ra cách để làm điều này. Dường như có sự liên kết rất chặt chẽ giữa ngữ cảnh điều khiển, khung nhìn, dữ liệu xem, dữ liệu định tuyến và phương thức render html.

Về cơ bản để thực hiện điều này, bạn cần tạo tất cả các yếu tố bổ sung này. Một số trong số chúng tương đối đơn giản (chẳng hạn như dữ liệu xem) nhưng một số phức tạp hơn một chút - ví dụ dữ liệu định tuyến sẽ xem xét trang WebForms hiện tại sẽ bị bỏ qua.

Vấn đề lớn dường như là các trang HttpContext - MVC dựa trên một HttpContextBase (thay vì HttpContext như WebForms làm) và trong khi cả hai triển khai thực hiện IServiceProvider chúng không liên quan. Các nhà thiết kế của MVC đã quyết định không thay đổi WebForms cũ để sử dụng cơ sở ngữ cảnh mới, tuy nhiên họ đã cung cấp một trình bao bọc.

này hoạt động và cho phép bạn thêm một cái nhìn cục bộ đến một 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); 
    } 

} 

Sau đó, trong WebForm của bạn, bạn có thể làm điều này:

<% WebFormMVCUtil.RenderPartial("ViewName", this.GetModel()); %> 
+1

Điều này làm việc một yêu cầu trang cơ bản, nhưng view.Render() thổi lên với "Xác nhận của MAC Viewstate thất bại ..." nếu bạn làm bất kỳ bài đăng trên trang container. Bạn có thể xác nhận như vậy không, Keith? –

+0

Tôi không nhận được rằng lỗi tuyến tính - tuy nhiên tôi nghĩ rằng nó sẽ xảy ra là xem một phần mà bạn đang vẽ bao gồm bất kỳ điều khiển WebForm. Phương thức RenderPartial này kích hoạt khi render - sau bất kỳ viewstate nào. Điều khiển WebForm bên trong khung nhìn từng phần sẽ bị hỏng và nằm ngoài vòng đời trang thông thường. – Keith

+0

Thực ra tôi có bây giờ - dường như nó xảy ra đối với một số hệ thống phân cấp điều khiển WebForms chứ không phải cho những người khác. Kỳ lạ là lỗi được ném từ bên trong các phương thức render MVC, như thể gọi bên dưới đến Page. Render đang mong đợi để thực hiện xác nhận MAC của trang và sự kiện, điều này sẽ luôn hoàn toàn sai trong MVC. – Keith

20

cách rõ ràng nhất sẽ được thông qua AJAX

một cái gì đó như thế này (sử dụng jQuery)

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

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

Từ câu hỏi: Nó sẽ hoạt động với javascript bị vô hiệu hóa. –

+9

đã được thêm vào sau câu trả lời của tôi) -: –

5

Dưới đây là một cách tiếp cận tương tự mà đã làm việc cho tôi . Chiến lược là để hiển thị khung nhìn một phần thành một chuỗi, sau đó xuất ra trong trang 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(); 
    } 
} 

Trong codebehind trang, bạn có thể làm

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

    } 
} 

và trong trang, bạn sẽ có quyền truy cập vào các nội dung trả

<%= NavigationBarContent %> 

Hy vọng rằng sẽ giúp!

+0

Điều này thực sự tuyệt vời, đặc biệt khi bạn có thể đặt các khối tập lệnh ở đâu đó! – jrizzo

8

Điều này thật tuyệt vời, cảm ơn!

Tôi đang sử dụng MVC 2 trên .NET 4, yêu cầu TextWriter được chuyển vào ViewContext, vì vậy bạn phải chuyển qua httpContextWrapper.Response.Output như được hiển thị bên dưới.

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); 
    } 
+1

Điều này chỉ hoạt động khi tôi KHÔNG cast Chế độ xem tới WebFormView. –

1

Giải pháp này có cách tiếp cận khác. Nó định nghĩa một System.Web.UI.UserControl có thể được đặt trên bất kỳ Biểu mẫu Web nào và được định cấu hình để hiển thị nội dung từ bất kỳ URL nào… bao gồm một phần xem MVC. Cách tiếp cận này tương tự như lời gọi AJAX cho HTML trong các tham số đó (nếu có) được đưa ra thông qua chuỗi truy vấn URL.

Đầu tiên, xác định một điều khiển người dùng trong 2 tập tin:

/controls/PartialViewControl.ascx nộp

<%@ 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); 
     } 
    } 
} 

Sau đó thêm điều khiển người dùng tới trang biểu mẫu web của bạn:

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

Tôi nghĩ đây là câu trả lời hay nhất, bạn có thể tái sử dụng UserControl nếu bạn định dùng nhiều hơn một lần, chỉ cần thay đổi contentUrl, tôi chỉ khuyên rằng requestPath hiện tại không nhận được Port, nếu trong trường hợp bạn sử dụng một cổng 80 khác, nó sẽ tăng một lỗi. – Daniel

+0

Tôi đã tìm thấy một vấn đề với nó, phương pháp này tạo ra một phiên mới cho yêu cầu. Vì vậy, nó giống như có hai trang web làm việc tại cùng một vị trí. – Daniel

+0

Có, nếu bạn đang sử dụng các phiên phía máy chủ để giữ trạng thái ứng dụng của bạn, giải pháp này sẽ không hoạt động. Tuy nhiên, tôi thích duy trì trạng thái trên máy khách. –

30

Mất một lúc, nhưng tôi đã tìm thấy một giải pháp tuyệt vời. Vì giải pháp Keith làm việc cho rất nhiều người, nhưng trong một số trường hợp, nó không phải là tốt nhất, bởi vì đôi khi bạn muốn ứng dụng của mình trải qua quá trình điều khiển để hiển thị chế độ xem và giải pháp của Keith chỉ hiển thị chế độ xem với một mô hình nhất định Tôi trình bày ở đây một giải pháp mới sẽ chạy quy trình bình thường.

bước chung:

  1. Tạo một lớp Utility
  2. Tạo một điều khiển Dummy với một cái nhìn giả
  3. Trong aspx hoặc master page của bạn, gọi phương thức tiện ích để làm cho một phần đi qua các điều khiển, xem và nếu bạn cần, mô hình để hiển thị (dưới dạng đối tượng),

Hãy kiểm tra chặt chẽ trong ví dụ này

1) tạo ra một lớp gọi là MVCUtility và tạo ra các phương pháp sau đây:

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

Tạo một lớp để thông qua các thông số, tôi sẽ gọi đây RendeActionViewModel (bạn có thể tạo ra trong cùng một tập tin của lớp MvcUtility)

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

2) Bây giờ, tạo một điều khiển tên DummyController

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

    } 

Tạo một cái nhìn Dummy gọi PartialRender.cshtml (xem dao cạo) cho DummyController với các nội dung sau đây, lưu ý rằng nó sẽ thực hiện một Render hành động bằng cách sử dụng helper Html

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

3) Bây giờ chỉ cần đặt này trong MasterPage hoặc aspx của bạn tệp, để hiển thị một phần chế độ xem mà bạn muốn. Lưu ý rằng đây là một câu trả lời tuyệt vời khi bạn có nhiều chế độ xem của dao cạo mà bạn muốn trộn với các trang MasterPage hoặc aspx của mình.(Suposing chúng tôi có một PartialView gọi nhập cho Controller Trang chủ

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

hoặc nếu bạn có một mô hình để thông qua vào Action

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

Giải pháp này là rất tốt, không sử dụng ajax gọi , mà sẽ không gây ra một trì hoãn làm cho quan điểm lồng nhau, nó doens't làm cho một WebRequest mới nên sẽ không mang lại cho bạn một phiên mới, và nó sẽ xử lý phương pháp để lấy các ActionResult cho quan điểm mà bạn muốn, nó hoạt động mà không đi bất kỳ mô hình

NhờUsing MVC RenderAction within a Webform

+1

Tôi đã thử tất cả các giải pháp khác trong bài đăng này và câu trả lời này là tốt nhất. Tôi muốn giới thiệu cho bất cứ ai khác để thử giải pháp này đầu tiên. – Halcyon

+0

Nó sẽ không hoạt động nếu tôi muốn thực hiện yêu cầu đăng bài. – maxspan

+0

Hi daniel. Bạn có thể giúp tôi không. Tôi đã làm theo lời giải của bạn nhưng tấn công vào một nơi. Tôi đã nêu ra nó dưới http://stackoverflow.com/questions/38241661/showing-a-razor-view-in-an-web-form-iframe –

Các vấn đề liên quan