After the troubles we faced working with Crystal Reports in the last sprint my manager gave me the permission to go and spike Active Reports .NET to see what I could find… so far… so good! Right off the bat the report designer is a lot like the Win Forms designer. You can drag and drop Active Reports.NET controls like the “Label”, “TextBox”, “CheckBox”, and “RichTextBox” controls. This generates a “.designer.cs” file, just like a win form. active_reports_toolbox
I like this because it’s intuitive, I already have knowledge working with WinForms so I don’t have go learn how a completely new designer behaves. Also I have access to the code behind. (I can see what’s going on!) Reports seem to be super easy to build. Each report inherits from “ActiveReport3” and can be access almost like any ol’ “plain old clr object” (POCO). The quick and easy from running a report might look something like…

SimpleReport report = new SimpleReport( );
  report.DataSource = CreateDtosList( );
  report.Run( false );
  this.uxReportViewerControl.Document = report.Document;

The “SimpleReport” type inherits from “ActiveReport3” and represents the report that we want to view. The DataSource property can be bound to a type that implements the IEnumerable interface. The call to “Run” binds the data to the report and creates an in memory representation of the report which can then be bound a report viewer control which is what takes place in the following line.

That’s the quick and dirty… The above images shows the report viewer control a table of contents panel. This panel will show any “bookmarks” that are tagged in the report. To set a bookmark in different sections of a report you can create a handler for the “Format” event of the detail section of the report. Each report can have a report header/footer, group header/footer and a detail section. (You can embed multiple sub reports into a detail section)

public partial class SimpleReport : ActiveReport3 
  {
      public SimpleReport( ) 
      {
          InitializeComponent( );

          this.detail.Format += delegate { detail.AddBookmark( uxQuestionText.Value.ToString( ) ); };
      }

The above code sample creates a handler for the “Format” event and adds a bookmark to the detail section with the name of the question. My feeling after spiking Active Reports is that I like it. I’m sure I haven’t covered everything that it has to offer, but I have confidence that I could use it the way i need it to work. The way any good tool should work. I really enjoy being able to access the object model and the ability to bind custom collections to the view. I like how the development seems to familiar to a winforms model, and I feel that given some creativity you can use Active Reports .NET the way that you want to use it, not the way it wants you to use it. After tinkering with the quick and dirty, I started to try to evolve to something a little more extensible. My pair and I thought of perhaps modelling the report as a type that contains a listing of sections. E.g. a report has a header section, and a footer section and a table of contents section. We started moving towards a model where a section builder would build a section and bind the necessary information to that section. The section builder would then add the section the report. Here’s kind of what we came up with…

public class ResultsSectionBuilder : ISectionBuilder 
{
      public ResultsSectionBuilder( IResultSectionTask task ) 
      {
          _task = task;
      }

      public void BuildFrom( IMainReport report ) 
      {
          BuildFrom( report, new ResultsSection( ) );
      }

      public void BuildFrom( IMainReport report, IResultsSection section ) 
      {
          report.AddSection( section.BindTo( _task.GetResults( ) ) );
      }

      private readonly IResultSectionTask _task;
  }

The idea is that when the report is run, the report will ask it’s section builder to build their sections and add it to it. Maybe this might flush it out a bit more…

 1 public class MainReport : IMainReport 
 2   {
 3       public MainReport( ) : this( new List< ISectionBuilder >( ) ) {}
 4 
 5       public MainReport( IList< ISectionBuilder > builders ) 
 6       {
 7           _builders = builders;
 8           _sections = new List< IReportSection >( );
 9       }
10 
11       public void AddSection( IReportSection section ) 
12       {
13           _sections.Add( section );
14       }
15 
16       public IMainReport Run( ) 
17       {
18           foreach( ISectionBuilder builder in _builders ) 
19           {
20               builder.BuildFrom( this );
21           }
22           return this;
23       }
24 
25       private readonly IList< ISectionBuilder > _builders;
26   }

Now that I’m typing this out, I’m thinking that this is a form of double dispatch, which could move to a visitor pattern. I have to admit I’m a little shy of the visitor because it seems awkward. But after reading Jeremy Millers post last night, I’m questioning whether I need to better understand how to use it or recognize good uses of it. We never completely flushed out this work, but hopefully it sparks some ideas… Source code (5.4 MB) I hope this helps you to think about how to build more extensible reports. I still have a lot to learn in this category, but I know you can build reports that don’t rely on data sets. Hopefully, time will tell me which is more practical and worth investing time into. One last thought, I really like how you can just download the active reports assemblies and start building learning without a license. The licensing model seems to be great, when you work with the trial version you get a little caption on the report that indicates that a trial version is being used. If you like the reports and working with it, you can go out and buy yourself a developer license and deploy as you like. It really does seem like Active Reports was built as a tool to aid the developer as opposed to trying to squeeze out pennies out of end users. Reference material:

comments powered by Disqus