How to include a partial view inside a webform

后端 未结 7 2211
灰色年华
灰色年华 2020-11-28 01:07

Some site I\'m programming is using both ASP.NET MVC and WebForms.

I have a partial view and I want to include this inside a webform. The partial view has some code

相关标签:
7条回答
  • 2020-11-28 01:41

    It took a while, but I've found a great solution. Keith's solution works for a lot of people, but in certain situations it's not the best, because sometimes you want your application to go through the process of the controller for rendering the view, and Keith's solution just renders the view with a given model I'm presenting here a new solution that will run the normal process.

    General Steps:

    1. Create a Utility class
    2. Create a Dummy Controller with a dummy view
    3. In your aspx or master page, call the utility method to render partial passing the Controller, view and if you need, the model to render (as an object),

    Let's check it closely in this example

    1) Create a Class called MVCUtility and create the following methods:

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

    Create a class for passing the parameters, I will call here RendeActionViewModel (you can create in the same file of the MvcUtility Class)

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

    2) Now create a Controller named DummyController

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

    Create a Dummy view called PartialRender.cshtml (razor view) for the DummyController with the following content, note that it will perform another Render Action using the Html helper.

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

    3) Now just put this in your MasterPage or aspx file, to partial render a view that you want. Note that this is a great answer when you have multiple razor's views that you want to mix with your MasterPage or aspx pages. (supposing we have a PartialView called Login for the Controller Home).

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

    or if you have a model for passing into the Action

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

    This solution is great, doesn't use ajax call, which will not cause a delayed render for the nested views, it doesn't make a new WebRequest so it will not bring you a new session, and it will process the method for retrieving the ActionResult for the view you want, it works without passing any model

    Thanks to Using MVC RenderAction within a Webform

    0 讨论(0)
  • 2020-11-28 01:42

    This is great, thanks!

    I'm using MVC 2 on .NET 4, which requires a TextWriter gets passed into the ViewContext, so you have to pass in httpContextWrapper.Response.Output as shown below.

        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);
        }
    
    0 讨论(0)
  • 2020-11-28 01:48

    I had a look at the MVC source to see if I could figure out how to do this. There seems to be very close coupling between controller context, views, view data, routing data and the html render methods.

    Basically in order to make this happen you need to create all of these extra elements. Some of them are relatively simple (such as the view data) but some are a bit more complex - for instance the routing data will consider the current WebForms page to be ignored.

    The big problem appears to be the HttpContext - MVC pages rely on a HttpContextBase (rather than HttpContext like WebForms do) and while both implement IServiceProvider they're not related. The designers of MVC made a deliberate decision not to change the legacy WebForms to use the new context base, however they did provide a wrapper.

    This works and lets you add a partial view to a 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 );
        }
    
    }
    

    Then in your WebForm you can do this:

    <% WebFormMVCUtil.RenderPartial( "ViewName", this.GetModel() ); %>
    
    0 讨论(0)
  • 2020-11-28 01:56

    most obvious way would be via AJAX

    something like this (using jQuery)

    <div id="mvcpartial"></div>
    
    <script type="text/javascript">
    $(document).load(function () {
        $.ajax(
        {    
            type: "GET",
            url : "urltoyourmvcaction",
            success : function (msg) { $("#mvcpartial").html(msg); }
        });
    });
    </script>
    
    0 讨论(0)
  • 2020-11-28 01:56

    FWIW, I needed to be able to render a partial view dynamically from existing webforms code, and insert it at the top of a given control. I found that Keith's answer can cause the partial view to be rendered outside the <html /> tag.

    Using the answers from Keith and Hilarius for inspiration, rather than render direct to HttpContext.Current.Response.Output, I rendered the html string and added it as a LiteralControl to the relevant control.

    In static helper class:

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

    In calling class:

        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);
        }
    
    0 讨论(0)
  • 2020-11-28 02:08

    Here's a similar approach that has been working for me. The strategy is to render the partial view to a string, then output that in the WebForm page.

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

    In the page codebehind, you can do

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

    and in the page you'll have access to the rendered content

    <%= NavigationBarContent %>
    

    Hope that helps!

    0 讨论(0)
提交回复
热议问题