Home > .net 2.0, c# > ActiveReports 3.0 and Business Layer logic

ActiveReports 3.0 and Business Layer logic

February 7, 2007

So, after using 2.0 for quite a while, I finally had a project that needed advanced reporting of the PDF kind and decided to hit it up with ActiveReports 3.0.  Good times, installed, used my upgrade key, and I was off… and then not.

3.0 totally redesigned the architecture of how the reports, the designer, and underlying technology operated.  DataDynamics has a list of what changed… but I didn’t really realize the breadth of the changes.

The first thing you notice using 3.0 is the lack of .rpx files—everything is compiled in App_Data as .cs source files.  Cool beans.  The downfall—breaking those AR specific sections, specifically InitializeComponent();, breaks EVERYTHING and your designer will never render again.  Also, because everything (locations of objects, values, etc) is stored as global resources in App_GlobalResources, there is a LOT of wait time while developing due to refractoring (aka: every time you move something, get ready to wait).  I haven’t had time to throughly search their forums or report a problem—but it’s an annoying ‘feature’.

There is, however, a kink—using pre-defined business logic that is NOT an DataSet object or an ObjectDataSource.  There is a way, at least that I found, to do it without violating DataDynamics “Three Important ActiveReports Design Rules”.

Here’s a scenario:

You have your business layer, data layer, and presentation layer (web, in this case) separated out nicely.  All calls from your presentation layer go to the business layer, and business layer to your data layer, and data layer to your data sources (either typed DataSets or database queries using the Enterprise Library’s DAAB).

However, the DataSource options in ActiveReports is somewhat… limited—XML, SQL query, OLE DB query, and Unbound.  Unbound is basically what you want, but the help is still approaching it from a DataReader and OLE DB point of view.

Ar_datasource

So, the solution?  A bit of coding—as usual.

Step #1 – Creating your report viewer.

This entails passing things such as the header information and report id—things that our subreports will build on.

For a better understanding, my reports are very layered…

– A Viewer ASPX page calls a MasterReport.  The master report takes care of page header and footer and sets the ground work for subreports.

– Each subreport contains specific logic for what it is rendering—if it’s a section of data or what have you.

So, on our Viewer.aspx page, we have the following code in the Page_Load method:

protected void Page_Load(object sender, EventArgs e)
{
        MemoryStream mStream = new MemoryStream();

        MasterReport rpt = new MasterReport ();
        rpt.ReportId = _reportId;
        rpt.Run();
        
        PdfExport mypdfExport = new PdfExport();

        mypdfExport.Export(rpt.Document, mStream);
        mStream.Position = 0;
        Response.ContentType = "application/pdf";
        Response.AddHeader("content-disposition", "inline; filename=MyExport.pdf");
        Response.BinaryWrite(mStream.ToArray());
        Response.End();
}

This page simply sets the stage, generates the report, exports it to PDF, and then streams it to the client via HTTP.  The only public variable I’ve set here is the _reportId.  No data, nothing else… that’s taken care of in the actual report.  Take note of the ReportId property.  That public variable needs to be created on your master report we create later (if even necessary—it depends on your data structure).

We’re ready to create our Master Report.

Step #2 – Creating the master report and prepping it for subreports.

Adding a new object to your project and selecting the Active Reports file prompts you to place it in your App_Data directory.  Simple enough.

Ar_newreport

So, now we have a new report… with new objects to place on it… and DataAccess components.  As I said earlier, note that those are just the Table Adapters and XSD objects—not our business logic.  Let’s let those be for now.  You could also read this as “David didn’t have time to sit and dink with these because he couldn’t find documentation or methods to use them… so he worked around it.”.

So, first off, set up your variables.  Simple get/set’s for the public and a private identifier are just fine.

The real trick is to realize how to access the overall report’s methods.  For my UI, the “not on the page” space is a dark grey… you can see it in the above image.  Click on that and then go to Events in the Properties window.

Ar_events

DataInitialize, detail_Format, and ReportStart will become your best friends.  To add the event’s method to your code-behind, simply double-click on it.  Start with ReportStart.

According to the manual (available at http://www.datadynamics.com/Help/ARNET3/ActiveReports3_start.htm, but use the PDF version downloaded from http://www.datadynamics.com/Downloads/arn3pdf.zip, searching it is 10x faster and it’s far easier to read), ReportStart is the first event to fire, so that’s where we want to place our one-time renderings—report header, etc.  But, to do that, we need a bit of (business) logic.

I’ve used the designer and added four TextBox objects—txtName, txtBuilding, txtPeriod, and txtSupervisor.  Now, to populate those at runtime.


private void StudentReport_ReportStart(object sender, EventArgs e)
{
        DataRow _ReportData = 
            BusinessLogic.Reports.GetReportbyReportId(_reportid).Rows[0];
        txtName.Text = _ReportData["str_fullname"].ToString();
        txtBuilding.Text = _ReportData["int_buildingid"].ToString();
        txtPeriod.Text = _ReportData["int_reportquarter"].ToString() + 
            " - " + _ReportData["int_reportyear"].ToString();
        txtSupervisor.Text = _ReportData["str_supervisor"].ToString();
}

Of course, at this point, you would add some try/catch’s in, but this suffices.  So, we’ve populated our text boxes.  However, we need to add more information—a subreport of sectional information, say, the weather in select cities (it’s cold and … cold most everywhere in the US at the moment—so pick warm places!).

Add a subreport object to the page in the details area (note it’s ‘details’ now and not ‘Details’… oh the odd things that get changed) and give it the name subreportWeather.

Step #3 – Creating a subreport.

This part is actually easy—just create ANOTHER report.   In the last step, we were going to discuss the weather, so name it sub_Weather.cs or something similar.

Again, add your fields, such as the txtLocation, txtTemperature, txtForecast, etc.  Also, add any public and private variables that you’ll need.  Again, I pass through the report Id so I have it to work with.  I’d assume that you could use ParentReport.[something], but I could never get Intellisense to pick up on the parent’s private variables.

This time, however, after we create our DataTable in ReportStart, we need to iterate through it using DataInitialize to add fields to our report.  You could do this by hand… but… yeah, why?


    private void sub_Weather_ReportStart(object sender, EventArgs e)
    {
        _Weather = BusinessLogic.Weather.GetWeatherDataTable();
    }
    private void sub_Weather_DataInitialize(object sender, EventArgs e)
    {
        foreach (DataColumn column in _Weather.Columns)
        {
            Fields.Add(column.ColumnName);
        }
    }

Now, we’re ready to add data.  For this, we’ll use detail_Format.  To add this method, simply double-click on the details are of the report.  Here, you must remember that it fires this event off for each ROW in your data.

    private void detail_Format(object sender, EventArgs e)
    {
        txtLocation.Text = (Fields["str_location"] != null)
                                       ? Fields["str_location"].Value.ToString()
                                       : "";
        txtTemperature.Text = (Fields["str_temp"] != null)
                                       ? Fields["str_temp"].Value.ToString()
                                       : "";
        txtForecast.Text = (Fields["str_forecast"] != null)
                                       ? Fields["str_forecast"].Value.ToString()
                                       : "";
    }

Now, our subreport is complete, we’re ready to associate it on our master report.  Go back there and let’s call it from the ReportStart method.

sub_Weather weather = new sub_Weather();
weather.DataSource = BusinessLogic.Weather.GetWeatherDataTable();
weather.ReportId = _reportid;
subreportWeather.Report = weather;

Start your report!

Categories: .net 2.0, c#
  1. February 20, 2007 at 7:29 pm

    BTW, you do not need the detail_Format code at all in your subreport. You can simply set the DataField property of each textbox to the name of the field (as you named it in DataInitialize) That way the report would do the binding for you and it will handle the null value properly too. And the result will be slightly faster.

  1. No trackbacks yet.
Comments are closed.
%d bloggers like this: