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.

Sunday, January 24, 2010

ActiveReports6 : Viewer ToolBar Secrets.

Preface
One of the cool new features of ActiveReports6 is the updated End User Designer toolbar architecture. Now the public API exposes the methods which allow to get the designer's toolbar groups and they are ToolStrips. Which opens the wide capabilities of the toolbar customization. That is fine but the question which we recently got from the clients is "What about ActiveReports6 Windows Viewer toolbar?". The way to customize the viewer toolbar seems to be unchangeable since from ActiveReports for .NET 1.0 times! ActiveReports still allows to customize the viewer toolbar using the "Toolbar API" only. Toolbar API is not the best solution ever, it is pretty limited. Fortunately a bit of digging around ActiveReporrs6 viewer internals can provide us with more comfortable way of toolbar customization.

Viewer Toolbar is actually ToolStrip!
Let's see which child controls are hosted by ActiveReports6 Viewer control. Here is the simple code which performs that:

var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
foreach(Control child in viewer.Controls)
   Debug.WriteLine(string.Format("name:{0}, type{1}", child.Name, child.GetType()));

That code emits the following output:

name:, type#op.#7s
name:tocSplitter, typeSystem.Windows.Forms.Splitter
name:TableOfContents, typeDataDynamics.ActiveReports.Viewer.TableOfContents
name:, typeSystem.Windows.Forms.ToolStrip

The first control is one which shows the report pages, the third control is one which shows the TOC, the second control is the separator between the 1st and 3rd controls and 4th control is...yes, that is viewer toolbar! In ActiveReports6 the Windows Viewer toolbar is internally implemented using ToolStrip API. Cool, let's write the simple extension method which would provide us with access to viewer's Toolbar which is the ToolStrip:


public static class ViewerHelper
{
   public static ToolStrip CreateToolStrip(this Viewer viewer)
   {
      foreach (var control in viewer.Controls)
         if (control is ToolStrip)
            return (control as ToolStrip);
      return null;
   }
}

Now we can access to viewer's toolbar using the code like:

var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
ToolStrip viewerToolStrip = viewer.CreateToolStrip();

Let's see how we can implement several customization scenarios using this "secret" feature.

Scenario #1 - Composing the complex viewer toolbar
The following code builds the top panel of the tool strip container by putting File Menu and Viewer Toolbar together:

var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
ToolStrip viewerToolStrip = viewer.CreateToolStrip();
var ms = new MenuStrip {Dock = DockStyle.Top};
var fileMenu = new ToolStripMenuItem("File");
var fileOpenMenu = new ToolStripMenuItem("Open", null, delegate{/*opening RDF file happens here*/});
var fileExitMenu = new ToolStripMenuItem("Exit", null, delegate {Application.Exit(); });
fileMenu.DropDownItems.Add(fileOpenMenu);
fileMenu.DropDownItems.Add(fileExitMenu);
ms.MdiWindowListItem = fileMenu;
ms.Items.Add(fileMenu);
toolStripContainer1.TopToolStripPanel.Join(ms,1);
toolStripContainer1.TopToolStripPanel.Join(viewerToolStrip, 2);

Scenario #2 - Customizing the native toolbar items
The following code removes the text from Print, Backward and Forward buttons and also removes Annotations button from the viewer toolbar:

var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
ToolStrip viewerToolStrip = viewer.CreateToolStrip();
viewerToolStrip.Items[2].Text = viewerToolStrip.Items[20].Text = viewerToolStrip.Items[21].Text = string.Empty;
viewerToolStrip.Items.RemoveAt(23);

Advanced scenario.
In the last code sample I used the magic numbers 2,20,21 and 23 to access to the toolbar items. How do I know those magic numbers? I got them by running the simple code which goes through the toolbar items and outputs the index, text and type of each item. Here is the resulting list:

0 Table Of Contents:System.Windows.Forms.ToolStripButton
1 :System.Windows.Forms.ToolStripSeparator
2 &Print...:System.Windows.Forms.ToolStripButton
3 :System.Windows.Forms.ToolStripSeparator
4 Copy:System.Windows.Forms.ToolStripButton
5 :System.Windows.Forms.ToolStripSeparator
6 &Find:System.Windows.Forms.ToolStripButton
7 :System.Windows.Forms.ToolStripSeparator
8 Single Page View:System.Windows.Forms.ToolStripButton
9 Multiple Page View:#is.#5ml
10 Continuous Scroll:System.Windows.Forms.ToolStripButton
11 :System.Windows.Forms.ToolStripSeparator
12 Zoom Out:System.Windows.Forms.ToolStripButton
13 Zoom In:System.Windows.Forms.ToolStripButton
14 100 %:System.Windows.Forms.ToolStripComboBox
15 :System.Windows.Forms.ToolStripSeparator
16 Previous Page:System.Windows.Forms.ToolStripButton
17 Next Page:System.Windows.Forms.ToolStripButton
18 1/1:#is.#rs+#U.#ss
19 :System.Windows.Forms.ToolStripSeparator
20 &Backward:System.Windows.Forms.ToolStripButton
21 &Forward:System.Windows.Forms.ToolStripButton
22 :System.Windows.Forms.ToolStripSeparator
23 Annotations:System.Windows.Forms.ToolStripDropDownButton

As you can see most of the toolbar items are buttons and separators. There are two mysterious items though in the list. Item 9 is one which allows to set the mode of multi-page view in the viewer. I will not focus on this element for now.
Item 18 is the text box which shows the current page number and the page total of the report which is shown by viewer. Access to the properties and events of that text box is sometimes needed. As you can see this toolbar item is the instance of some internal class of Viewer assembly. We can't access to the properties and events of that internal class, but we can try cast it to ToolStripTextBox:


var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
ToolStrip viewerToolStrip = viewer.CreateToolStrip();
var pagesEditor = viewerToolStrip.Items[18] as ToolStripTextBox;
pagesEditor.TextChanged += delegate { Debug.WriteLine("The current page was changed"); };

..and that works.

Disclaimer
The features which are described in this article are not officially supported. They are not tested and documented. They can change in further versions. Use them at your own risk.

Saturday, January 16, 2010

Return to the blog

It's almost one year since from I made the last post in this blog. A lot of dramatic - good and terrible - events happened with me in 2009. I quit smoking, it was unexpectedly easy to do. Now I think that I was a complete idiot when was smoking twenty cigarettes per day. I almost quit drinking. Last two events where I drank the alcohol were the NY celebration and my mother birthday. My wedding plans were broken and me and my girlfriend stopped our relationships. I moved to the new apartments. My cat moved to my sister's house. I visited Bulgaria during my vacation. It's weird country! I started visiting gym twice per week. I lost about 5 kilograms of weight.
The worst thing that might happen did not happen - our company still alive and kicking! We saved our work. We are going to work on the new generation of the best reporting tool ever.
Anyway, all that stuff in the past. I hope that 2010 will be better than 2009. One of the things I plan is returning to the blog. I am getting a lot of ideas about which I would like to blog. I plan to shed the light to some aspects of ActiveReports 6 and Data Dynamics Reports internals. I got a lot of experience with ASP.NET MVC and have several topics to blog about. Finally I faced several questionable OOP decisions about which I want to blog as well.

C# dynamic types and the design patterns.

NOTES
This post is resurrected. I accidentally deleted this earlier.

Preface
The question that gives me no rest lately is how I can use C# 4.0 dynamic features in the real-world .NET applications. For a long time I had no ideas except of using dynamic objects in LINQ queries. It is briefly discussed in the comments to C# 4.0 - the first look at the DYNAMICITY post. But today I know another answer and it seems to be breathtaking. The answer is we don't need to use the OOP design patterns if we have the support of the "Dynamicity Forces".

Design patterns
Design patterns are part of the fundamental of Object Oriented Programming. Tremendous amount of resources have appeared on design patterns after the book Design Patterns: Elements of Reusable Object-Oriented Software was published in 1994. Each person involved in OOP programming was learning the design patterns. I used to use the design patterns in my every-day-work. Design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved. Technically that means that the type of the object which is passed to be handled by the application isn't known at compile time. Design patterns use the objects inheritance, composition, etc. to effectively solve the problem then the final object type isn't known at compile time. But wait...now we have the C# dynamic types which is intended to be used when the type of an object isn't known at compile time! Yes, I assert that any well-known design pattern can be avoided by using the dynamic types. Let me show the example.

Bridge design pattern(classic approach).
Bridge design pattern is very powerful and applies to many situations. Its purpose - "decouple an abstraction from its implementation so that the two can vary independently" - is not possible to understand without the real-world example. The classic example is shown below. I copied the code from the wikipedia article and updated it a bit(C# 3.0 is used).

internal interface IDrawingAPI
{
   void DrawCircle(double x, double y, double radius);
   void DrawLine(double x1, double y1, double x2, double y2);
}

internal class DrawingAPI1 : IDrawingAPI
{
   public void DrawCircle(double x, double y, double radius)
   {
      Console.WriteLine("API1.circle at {0}:{1} radius {2}", x, y, radius);
   }
   public void DrawLine(double x1, double y1, double x2, double y2)
   {
      Console.WriteLine("API1.line from {0},{1} to {2},{3}", x1, y1, x2, y2);
   }
}

internal class DrawingAPI2 : IDrawingAPI
{
   public void DrawCircle(double x, double y, double radius)
   {
      Console.WriteLine("API2.circle at {0}:{1} radius {2}", x, y, radius);
   }
   public void DrawLine(double x1, double y1, double x2, double y2)
   {
      Console.WriteLine("API2.line from {0},{1} to {2},{3}", x1, y1, x2, y2);
   }
}

internal interface IShape
{
   void Draw();
}

internal sealed class LineShape : IShape
{
   public double X1 { get; set; }
   public double X2 { get; set; }
   public double Y1 { get; set; }
   public double Y2 { get; set; }
   public IDrawingAPI DrawingAPI { get; set; }
   public void Draw()
   {
      DrawingAPI.DrawLine(X1, Y1, X2, Y2);
   }
}

internal class CircleShape : IShape
{
   public IDrawingAPI DrawingAPI{ get; set;}
   public double X{ get; set;}
   public double Y{ get; set;}
   public double Radius{ get; set;}
   public void Draw()
   {
      DrawingAPI.DrawCircle(X, Y, Radius);
   }
}

internal class BridgePatternClient
{
   public static void Main(string[] args)
   {
      var shapes = new IShape[2];
      shapes[0] = new CircleShape {X = 1, Y = 2, Radius = 3, DrawingAPI = new DrawingAPI1()};
      shapes[1] = new LineShape {X1 = 1, Y1 = 2, X2 = 3, Y2 = 4, DrawingAPI = new DrawingAPI2()};
      foreach (IShape shape in shapes)
      {
         shape.Draw();
      }
   }
}

In this example bridge design pattern is used to decouple the shape abstraction from the drawing implementation. Indeed there are alternate ways to design such a system, but these ways have the important design flaws. This is greatly described in Design Patterns Explained: A New Perspective on Object-Oriented Design book(must read!). Now let me show how I can accomplish the same task as shown above by using dynamic types.


Dynamicity in action.
The code that shown below is written in Visual Studio 2010 and it works fine. It's so simple that doesn't need to be explained. Just look at this.

internal sealed class DrawingAPI1
{
   public void DrawCircle(double x, double y, double radius)
   {
      Console.WriteLine("API1.circle at {0}:{1} radius {2}", x, y, radius);
   }
   public void DrawLine(double x1, double y1, double x2, double y2 )
   {
      Console.WriteLine("API1.line from {0},{1} to {2},{3}", x1, y1, x2, y2);
   }
}

internal sealed class DrawingAPI2
{
   public void DrawCircle(double x, double y, double radius)
   {
      Console.WriteLine("API2.circle at {0}:{1} radius {2}", x, y, radius);
   }
   public void DrawLine(double x1, double y1, double x2, double y2)
   {
      Console.WriteLine("API2.line from {0},{1} to {2},{3}", x1, y1, x2, y2);
   }
}

internal sealed class Line
{
   public double X1 { get; set; }
   public double X2 { get; set; }
   public double Y1 { get; set; }
   public double Y2 { get; set; }
   public dynamic DrawingAPI { get; set; }
   public void Draw()
   {
      DrawingAPI.DrawLine(X1, Y1, X2, Y2);
   }
   }

internal sealed class Circle
{
   public double X { get; set; }
   public double Y { get; set; }
   public double Radius { get; set; }
   public dynamic DrawingAPI { get; set; }
   public void Draw()
   {
      DrawingAPI.DrawCircle(X, Y, Radius);
   }
}

public class Program
{
   [STAThread]
   static void Main()
   {
      DrawingAPI1 api1 = new DrawingAPI1();
      DrawingAPI2 api2 = new DrawingAPI2();
      dynamic[] shapes = { new Line { X1 = 1, Y1 = 1, X2 = 2, Y2 = 3, DrawingAPI = api1 },
               new Circle { X = 10, Y = 20, Radius = 40.4f, DrawingAPI = api2 }
      };
      foreach (dynamic shape in shapes)
      {
         shape.Draw();
      }
   }
}

So, when we don't know the final type of the Shape and Drawing API we use dynamic type. Do you like it?


In conclusion.
This post is about dynamic features of C# of course. But it brings up more general question. In our days the programming technologies changes are incredible. It's good practice to keep up with Changes. But sometimes the changes can make us forgetting the good, old-fashioned technologies. I think that we should be careful with this effect. The new technologies can be awesome, greatly suitable for my needs. The old technologies can also be suitable for the problems I solve. A good problem-solver tries several possible solutions and selects the best one. A good problem-solver doesn't use the technology just because it is cutting-edge.