Saturday, January 8, 2011

Rendering Asp.Net WebForm via an MVC action

As part of our rewrite of WebForms to MVC we have some reports that are generated via Infragistics. I know I said in the previous post that the reason we're doing the rewrite is because we hate Infragistics. However, one thing that Infragistics does well is to give a lot of power to the developer for developing reports.

There is no GUI for report development and the documentation is virtually non existent, but if you need dynamic reports that are generated at run time it's a great library to use.

I won't bore you with implementation details for our reports, but the fact of the matter is that we need to render a WebForm. How do you do that inside a Controller action? Well fortunately the folks at Microsoft have thought of this scenario. There is an IView implementation called WebFormView. So if you need to return a classic Asp.Net Web Form as part of your action, you can do it like so.

public ActionResult Index()
{
   return View(new WebFormView("~/path/to/your/webform.aspx"));
}

The only catch is that your WebForm, must inherit from System.Web.Mvc.ViewPage, rather than System.Web.UI.Page. Behind the scenes it will call the BuildManager to build your web form and call the ProcessRequest method with HttpContext.Current. This means that the page is taken through the Asp.Net page life cycle.

You can even serve up WebControls like this. Again they have to inherit from System.Web.Mvc.ViewUserControl. There is one more limitation. That is that the user control can either only have html elements (albeit they can have the runat=server attribute). Or if you really need server controls, then the user control must contain a form with the runat=server attribute.

One final note. If for whatever reason you cannot change your web forms to inherit from ViewPage or you need to access the compiled instance of the Page, you can still use them to render content inside a controller action like so

public ActionResult Index()
{
   var page = BuildManager
.CreateInstanceFromVirtualPath("~/path/to/your/webform.aspx", typeof(Page));
   page.ProcessRequest(HttpContext.Current);
   return new EmptyResult();
}

20 comments:

  1. Thank you, just what I was looking for.

    ReplyDelete
  2. Hi Vadim. I get to your post looking for a way to interact in an MVC Application with Web Forms through Controllers and views.
    I tried your solution and it works partially.
    It seems that need the context of traditional ASP.NET pages for asinchronous operations (like the operations done by the ReportViewer). Have you any idea how to solve this???. I have this two codes trying to completely render the page in a Webform context:


    [ChildActionOnly]
    public ActionResult RenderWebFormReportViewer(ReportConfiguration configuration)
    {
    IHttpHandler page = (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath(configuration.ReportPath, typeof(Page));
    HttpApplication controllerContextHttpContextGetService = (HttpApplication)ControllerContext.HttpContext.GetService(typeof(HttpApplication));
    page.ProcessRequest(controllerContextHttpContextGetService.Context);
    return new EmptyResult();
    }



    [ChildActionOnly]
    public ActionResult RenderWebFormReportViewer(ReportConfiguration configuration)
    {
    Page page = BuildManager.CreateInstanceFromVirtualPath(configuration.ReportPath, typeof(Page)) as Page;
    HttpApplication controllerContextHttpContextGetService = (HttpApplication)ControllerContext.HttpContext.GetService(typeof(HttpApplication));
    page.ProcessRequest(controllerContextHttpContextGetService.Context);
    return new EmptyResult();
    }

    Best Regards!

    ReplyDelete
  3. @Carlos, what is the error you are getting?

    ReplyDelete
  4. thanks carlos reyes, congratulations =)

    ReplyDelete
  5. I don't see a manner to pass querystring parameters to the asp page in either of the methods listed. Is there a way to do so? Simply compositing those params into the URL causes resolution failure (404) as they are not expected. What about form data?

    ReplyDelete
    Replies
    1. What you could do is have your MVC action process the parameters, either via method arguments or via Request.QueryString. Then you could create your web form page using BuildManager. Cast it to the appropriate type (e.g. MyFormClass) and use property setters to set the parameters.

      public ActionResult Show(int id)
      {
      var page = (MyWebForm)BuildManager
      .CreateInstanceFromVirtualPath("~/path/to/your/webform.aspx", typeof(Page));
      page.Id = id;
      page.ProcessRequest(HttpContext.Current);
      return new EmptyResult();
      }

      Delete
    2. Hi Vadim,

      Is there a way of stopping someone directly navigating to the aspx, i.e. requiring them to come through the controller action. My thought is in terms of using custom authorize attributes and granular permissions which I have worked on extensively with MVC, plus I much prefer the structure behind MVC, but some scenarios require the odd web form or two.

      Cheers,

      Andy

      Delete
    3. Andrew, I'm not sure, but I think that you should be able to control that through your routes and security permissions in web.config. Failing that, you could add a property on your Form and use that to ensure that the user navigated to that form through your controller, otherwise you can redirect them to an appropriate place.

      public ActionResult Show(int id)
      {
      var page = (MyWebForm)BuildManager
      .CreateInstanceFromVirtualPath("~/path/to/your/webform.aspx", typeof(Page));
      page.ThroughController = true;
      page.ProcessRequest(HttpContext.Current);
      return new EmptyResult();
      }

      then in your Page_Load method you could do something like

      if (!ThroughController)
      Response.Redirect(AddressToRedirectTo);

      Delete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Does this method handle postbacks and states properly?

    ReplyDelete
    Replies
    1. No, I don't believe it does. If you need to actually use the web form as a web form, I would suggest running it alongside your mvc application (asp.net can run mvc alongside webforms) and using routing to give it a friendly url.

      Delete
  8. Thank you for your post. I am a beginner to intermediate level when it comes to web programming. I have a question related to rendering webform via action. Recently, in one of our projects, we came across to a situation where we need to display aspx page from action and we used Response.Redirect and that solved the issue.

    Now, I wanted to know what is the advantage that we gain by following this type of rendering (than redirect mechanism)? I am seeing so many posts on the interenet where nobody is refering Response.Redirect. Is this really bad?

    Please help me understanding the difference.

    Thanks a ton in advance for your time and response.

    ReplyDelete
    Replies
    1. Response.Redirect just redirects the user to your aspx page. There isn't much of a point in using your action at that stage. Just use MapPageRoute instead to route a particular URL to the aspx web form.

      http://msdn.microsoft.com/en-us/library/system.web.routing.routecollection.mappageroute.aspx

      Delete
  9. Can u plz tell me how to know the path of webform in my mvc web app?

    ReplyDelete