Sunday, January 31, 2010

The fresh glance at using ActiveReports 6 in ASP.NET MVC.

Preface
I am a big fan of ASP.NET MVC. I never liked developing of the web-applications using CGI, PHP, ASP, JSP and finally ASP.NET. Remember "The reason most people want to program for the web is that they're not smart enough to do anything else" discussion? I agree with Michael Braude. In other hands ASP.NET MVC provides developers with the solid framework with which you work with Model-View-Controller design pattern, use well-known practices to follow DRY principle, extend the native functionality to achieve the specific requirements, etc. It's just beautiful. Indeed I was excited about using ActiveReports and Data Dynamics Reports in ASP.NET MVC applications. We published the screencast which shows how to use the ActiveReports WebViewer in ASP.NET MVC application, but this way is not essential for ASP.NET MVC framework. In this article I am going to demonstrate how to create ActiveReports6 WebViewer control for ASP.NET MVC.

ASP.NET MVC and server-side controls.
The most annoying thing about ASP.NET MVC is it's not designed for use with the ASP.NET server-side controls. Custom Controls Everywhere and ASP.NET MVC totally covers that subject. Of course this article was written two years ago, but the main thought is still truth. ASP.NET MVC provides developers with the bunch of the helper methods which allow to create the UI and all the controls which you can create are client-side. The typical ASP.NET MVC approach to render the UI is shown here. Of course it is possible to use the server-side control on ASP.NET MVC applications. For example, we made sure that both of ActiveReports and Data Dynamics Reports web-viewer controls which are server-side ASP.NET controls work fine against the ASP.NET MVC framework. But the way which should be used to get them working is not essential...Imagine how it would be nice to just write the single line of code in ASP.NET MVC View page:

<%=Html.WebViewerFor("reportViewer", Model, new {width="100%", height="100%"}) %>

to get web-viewer working. Well, this is what we will achieve at the end of this article.

ActiveReports 6 Flash Viewer Secrets.
One of the cool new features of ActiveReports 6 is the new Flash-based web-viewer. Flash-based viewer loads RDF files and displays the report content. There is one problem about Flash Viewer though. The public API allows loading the report and customizing the UI using the server-side helpers only. We recently got a bit of criticism regarding the first problem and I provided the work around the problem. This turn around allows to render the content which displays flash viewer and specifies the report to show in the viewer and all those actions are performed on client-side. This Flash Viewer "secret" is the base for ActiveReports 6 ASP.NET MVC control.

XML-based reports and RPX handler.
Here I should note that all the further discussion and code suppose that the reports which are used in web-application are xml-based and RPX handler is set up. RPX handler provides the comfortable way to pass the report parameters and return the report is saved in RDF format. For example - there is xml-based Customers.rpx report which is saved in the root of my web-application. This report shows the list of the customers from the certain country. The country is specified via the report parameter. RPX HTTP handler allows to use the following URL to return the list of British customers in RDF format: http://<server url>/Customers.rpx?Country=Britain&OutputFormat=Rdf3

Creating the View Model for Flash Viewer.
The UI rendering in ASP.NET MVC deals with the Models and Views. The Model is the instance of some class which contains all the information which is needed to render the UI. For example, the list of the Orders is passed in the View and it renders the UI which shows HTML table where each row displays the Order details. Let's compose the class which encapsulates all the information which is needed for Flash Viewer in order to show the report. As I mentioned before, Flash Viewer accepts the RDF documents. To build the URL which returns the RDF document using RPX HTTP Handler we need to know the report name and the parameters values. So, the Flash Viewer View Model class is simple:

public class ReportViewModel
{
   public ReportViewModel()
   {
      ReportParameters = new Dictionary();
   }
   public string ReportName { get; set; }
   public Dictionary<string, object> ReportParameters { get; private set; }
}


Implementation of the HTML Helper for Flash Viewer Rendering.
The HTML Rendering Helper in ASP.NET MVC is the extension method for System.Web.Mvc.HtmlHelper class. This method should accept the instance of ReportViewModel class and other parameters, the code explains it better:

public static class FlashViewerExtensions
{
   public static string FlashViewerFor(this HtmlHelper htmlHelper, string name,       ReportViewModel model)
   {
      return FlashViewerFor(htmlHelper, name, model, null);
   }

   public static string FlashViewerFor(this HtmlHelper htmlHelper, string name,       ReportViewModel model, object htmlAttributes)
   {
      var tagBuilder = new TagBuilder("object");
      tagBuilder.MergeAttribute("type", "application/x-shockwave-flash");
      tagBuilder.MergeAttribute("id", name);
      // the following attribute is needed for non-IE browsers.
      tagBuilder.MergeAttribute("data", "/ActiveReports.FlashViewer.swf");
      if(htmlAttributes!=null)
      {
         IDictionary actualAttributes = new RouteValueDictionary(htmlAttributes);
         foreach(var key in actualAttributes.Keys)
         {
            tagBuilder.MergeAttribute(key, htmlHelper.Encode(actualAttributes[key]));
         }
      }
      var innerContent = new StringBuilder();
      innerContent.AppendLine(MovieParameter);
      innerContent.AppendLine(GetFlashVarsParameter(model, htmlHelper));
      tagBuilder.InnerHtml = innerContent.ToString();
      return tagBuilder.ToString(TagRenderMode.Normal);
   }

   private static string MovieParameter
   {
      get
      {
         if(_movieParameter == null)
         {
            var tagBuilder = new TagBuilder("param");
            tagBuilder.MergeAttribute("name", "movie");
            tagBuilder.MergeAttribute("value", "/ActiveReports.FlashViewer.swf");
            _movieParameter = tagBuilder.ToString(TagRenderMode.SelfClosing);
         }
      return _movieParameter;
      }
   }private static string _movieParameter;

   private static string GetFlashVarsParameter(ReportViewModel reportViewModel,       HtmlHelper htmlHelper)
   {
      var tagBuilder = new TagBuilder("param");
      tagBuilder.MergeAttribute("name", "FlashVars");
      string url = string.Format("URL={0}.rpx?OutputFormat=Rdf2",       reportViewModel.ReportName);
      foreach(string key in reportViewModel.ReportParameters.Keys)
      {
         url += string.Format("&{0}={1}", key,             reportViewModel.ReportParameters[key]);
      }
      tagBuilder.MergeAttribute("value", url);
      string ret= tagBuilder.ToString(TagRenderMode.SelfClosing);
      return ret.Replace("&amp;", "&");
   }
}

That's it! MovieParameter property returns <param name="movie" value="/ActiveReports.FlashViewer.swf" /> tag, the code supposes that ActiveReports.FlashViewer.swf seats in the root folder of the web-site. GetFlashVarsParameter method returns <param name="FlashVars" value="URL=url of the report" /> tag, it's built using the report name and the report parameters collection which are passed in the public FlashViewerFor methods. FlashViewerFor methods build <object> tag which would be displayed by any type of browser. The code uses System.Web.Mvc.TagBuilder class which is well-known practice.

The sample of usage.
Here is the code of the ASP.NET MVC Action which creates the instance of ReportViewModel class, passes that instance to the ASP.NET MVC View and returns the result:

public ActionResult Customers()
{
   var model = new ReportViewModel { ReportName = "/Reports/Customers" };
   return View("CustomersReport", model);
}

..and the code of "CustomersReport" View:

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Import Namespace="EmptyMvcApplication.Controllers" %>
<body>
<% =Html.FlashViewerFor("reportViewer", Model, new {width="100%", height="500"}) %>
</body>

Now, the Customers action of some controller returns the page which hosts flash viewer which shows Customers report.

Finally...
ASP.NET MVC allows developer to feel like the real developer during the creation of web-based applications. The sample of the extension which is shown in this article is just a little piece of the functionality which is available with ASP.NET MVC.

4 comments:

  1. Thank U Very much
    i tried it and it woks corretly but the viwer give me error "
    IOError while loading document. Reason: Error #2032"

    ReplyDelete
  2. i Just Forget to add handlers thank u very much
    :) :) :) :)

    ReplyDelete
  3. kenya and tanzania safariTarget Safaris is a leading provider of Africa Safaris, Kenya Safari vacations, Tanzania Safaris and Kenya Tanzania Safaris, experience your best African tour with us.

    ReplyDelete
  4. Pet odor removal CovingtonCarpet Cleaning in Seattle, Bellevue & Surrounding Areas - Our carpet cleaning Seattle, WA experts provide the most professional carpet cleaning services Seattle can offer. Contact us today
    Pet odor removal Mercer IslandCarpet Cleaning in Seattle, Bellevue & Surrounding Areas - Our carpet cleaning Seattle, WA experts provide the most professional carpet cleaning services Seattle can offer. Contact us today

    ReplyDelete