Niektóre witryny, które programuję, używają zarówno ASP.NET MVC, jak i formularzy internetowych.
Mam częściowy widok i chcę to uwzględnić w formularzu internetowym. Widok częściowy zawiera kod, który musi zostać przetworzony na serwerze, więc użycie Response.WriteFile nie działa. Powinien działać przy wyłączonym javascript.
Jak mogę to zrobić?
Odpowiedzi:
Rzuciłem okiem na źródło MVC, aby zobaczyć, czy mogę dowiedzieć się, jak to zrobić. Wydaje się, że istnieje bardzo ścisłe powiązanie między kontekstem kontrolera, widokami, danymi widoku, danymi routingu i metodami renderowania html.
Zasadniczo, aby to się stało, musisz stworzyć wszystkie te dodatkowe elementy. Niektóre z nich są stosunkowo proste (na przykład dane widoku), ale niektóre są nieco bardziej złożone - na przykład dane routingu będą uwzględniać ignorowanie bieżącej strony WebForms.
Wydaje się, że dużym problemem są strony HttpContext - MVC opierają się na HttpContextBase (a nie HttpContext, jak robią to WebForms) i chociaż oba implementują IServiceProvider, nie są powiązane. Projektanci MVC celowo podjęli decyzję, aby nie zmieniać starszych formularzy WebForm, aby korzystały z nowej bazy kontekstowej, jednak zapewnili opakowanie.
Działa to i umożliwia dodanie częściowego widoku do formularza internetowego:
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 ); } }
Następnie w swoim formularzu internetowym możesz to zrobić:
<% WebFormMVCUtil.RenderPartial( "ViewName", this.GetModel() ); %>
źródło
Trochę to trwało, ale znalazłem świetne rozwiązanie. Rozwiązanie Keitha działa dla wielu ludzi, ale w pewnych sytuacjach nie jest najlepsze, ponieważ czasami chcesz, aby aplikacja przeszła przez proces kontrolera w celu renderowania widoku, a rozwiązanie Keitha po prostu renderuje widok za pomocą danego modelu I ' Przedstawiam tutaj nowe rozwiązanie, które będzie działać normalnie.
Ogólne kroki:
aspx
lubmaster page
wywołaj metodę narzędziową, aby renderować częściowe przekazanie kontrolera, wyświetlić i, jeśli potrzebujesz, model do renderowania (jako obiekt),Sprawdźmy to dokładnie w tym przykładzie
1) Utwórz klasę o nazwie
MVCUtility
i utwórz następujące metody://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 }); }
Utwórz klasę do przekazywania parametrów, zadzwonię tutaj RendeActionViewModel (możesz utworzyć w tym samym pliku klasy MvcUtility)
public class RenderActionViewModel { public string ControllerName { get; set; } public string ActionName { get; set; } public object RouteValues { get; set; } }
2) Teraz utwórz kontroler o nazwie
DummyController
//Here the Dummy controller with Dummy view public class DummyController : Controller { public ActionResult PartialRender() { return PartialView(); } }
Utwórz widok Dummy o nazwie
PartialRender.cshtml
(widok brzytwy)DummyController
z następującą zawartością, zwróć uwagę, że wykona on inną akcję renderowania za pomocą pomocnika HTML.@model Portal.MVC.MvcUtility.RenderActionViewModel @{Html.RenderAction(Model.ActionName, Model.ControllerName, Model.RouteValues);}
3) Teraz po prostu umieść to w swoim pliku
MasterPage
lubaspx
, aby częściowo wyrenderować żądany widok. Pamiętaj, że jest to świetna odpowiedź, gdy masz wiele widoków maszynki do golenia, które chcesz połączyć ze swoimiMasterPage
lubaspx
stronami. (zakładając, że mamy widok PartialView o nazwie Logowanie do strony głównej kontrolera).<% MyApplication.MvcUtility.RenderAction("Home", "Login", new { }); %>
lub jeśli masz model do przejścia do Akcji
<% MyApplication.MvcUtility.RenderAction("Home", "Login", new { Name="Daniel", Age = 30 }); %>
To rozwiązanie jest świetne, nie używa wywołania ajax , które nie spowoduje opóźnionego renderowania dla zagnieżdżonych widoków, nie tworzy nowego żądania WebRequest, więc nie przyniesie ci nowej sesji i przetworzy metodę pobierania ActionResult dla żądanego widoku, działa bez przekazywania żadnego modelu
Dzięki zastosowaniu MVC RenderAction w formularzu internetowym
źródło
najbardziej oczywistym sposobem byłoby użycie AJAX
coś takiego (używając jQuery)
<div id="mvcpartial"></div> <script type="text/javascript"> $(document).load(function () { $.ajax( { type: "GET", url : "urltoyourmvcaction", success : function (msg) { $("#mvcpartial").html(msg); } }); }); </script>
źródło
To świetnie, dzięki!
Używam MVC 2 na .NET 4, który wymaga TextWriter jest przekazywany do ViewContext, więc musisz przekazać httpContextWrapper.Response.Output, jak pokazano poniżej.
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); }
źródło
Oto podobne podejście, które działa w moim przypadku. Strategia polega na renderowaniu częściowego widoku w postaci ciągu znaków, a następnie wyświetlaniu go na stronie formularza internetowego.
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(); } }
W kodzie strony możesz to zrobić
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); } }
a na stronie będziesz mieć dostęp do renderowanej treści
<%= NavigationBarContent %>
Mam nadzieję, że to pomoże!
źródło
To rozwiązanie ma inne podejście. Definiuje element,
System.Web.UI.UserControl
który można umieścić w dowolnym formularzu sieci Web i skonfigurować do wyświetlania zawartości z dowolnego adresu URL… w tym częściowego widoku MVC. To podejście jest podobne do wywołania AJAX dla HTML, w którym parametry (jeśli istnieją) są podawane za pośrednictwem ciągu zapytania URL.Najpierw zdefiniuj kontrolkę użytkownika w 2 plikach:
Plik /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); } } }
Następnie dodaj kontrolkę użytkownika do strony formularza internetowego:
<%@ 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/" />
źródło
FWIW, musiałem mieć możliwość dynamicznego renderowania częściowego widoku z istniejącego kodu formularzy internetowych i wstawienia go na górze danej kontrolki. Odkryłem, że odpowiedź Keitha może spowodować renderowanie częściowego widoku poza
<html />
tagiem.Korzystając z odpowiedzi od Keitha i Hilariusa jako inspiracji, zamiast renderować bezpośrednio do HttpContext.Current.Response.Output, wyrenderowałem ciąg html i dodałem go jako LiteralControl do odpowiedniej kontrolki.
W statycznej klasie pomocniczej:
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(); }
Na zajęciach:
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); }
źródło