http://www.developer.com/

Back to article

Reporting - Combining ReportViewer and MS Charts


May 20, 2009

Introduction

There is often a requirement in business applications to display charts to analyze data and create reports to document and distribute results from an application. Microsoft has developed several tools to meet these needs. Visual Studio comes with a Report type and a ReportViewer control that takes advantage of Microsoft Reporting Services. Additionally, you can download an add-on for .NET 3.5 SP1 and Visual Studio 2008 to support robust charts without purchasing expensive, proprietary software. We will use Visual Studio 2008 and .NET 3.5 SP1 to demonstrate a simple application that will display a chart and allow the user to create a report that can be saved or printed with the chart information. The Reporting Services framework offers a chart element that you can add to your reports, but we will be using a chart from the Microsoft Chart Controls. We have found that the Microsoft Chart Controls offer a lot of flexibility and ease-of-use that is lost with the built-in report chart element. In addition, with our method, you can reuse the chart in several places in your application as well as the report, reducing rework when a change needs to be made to a chart. In order to follow along with the example in this article, you will need to install Microsoft Chart Controls for Microsoft .NET Framework 3.5 and Microsoft Chart Controls Add-on for Microsoft Visual Studio 2008. If you are new to MS Charts, you can check out this overview to help you get started and set up.

To get started, create a new Windows Forms project called "Report Project Demo." We will be using simple classes to be used as the data source for the chart and report. Add two new classes, ReportData and SalespersonData. In the SalespersonData class add the following code:

      public class SalespersonData
      {
          public string Name { get; set; }
          public int SalesAmount { get; set; }
  
          public SalespersonData() { }
      }

In the ReportData class, add the following code and example data:

      public class ReportData
      {
          public List SalesData { get; set; }
  
          public ReportData()
          {
              SalesData = new List();
  
              SalespersonData sp1 = new SalespersonData();
              sp1.Name = "Bill";
              sp1.SalesAmount = 12000;
  
              SalespersonData sp2 = new SalespersonData();
              sp2.Name = "Susan";
              sp2.SalesAmount = 15000;
  
              SalespersonData sp3 = new SalespersonData();
              sp3.Name = "Mark";
              sp3.SalesAmount = 9000;
  
              SalespersonData sp4 = new SalespersonData();
              sp4.Name = "Becky";
              sp4.SalesAmount = 11000;
  
              SalesData.Add(sp1);
              SalesData.Add(sp2);
              SalesData.Add(sp3);
              SalesData.Add(sp4);
          }        
      }

The basic foundation for the data we are going to use is now in place. Keep in mind that all .NET data sources are supported by MS Charts. This means you can use arrays, data views, data tables, XML, etc.

Drag the Chart control from the Data section of the Toolbox onto the design surface of Form1. Rename the chart to chartSales. It will already have a default data series named "Series1" that we will be using.

Add the following using statement to the Form1 code-behind:

  using System.Windows.Forms.DataVisualization.Charting;

Since you have already placed the Chart control on the form, Visual Studio has added the DataVisualization reference to the project's References folder.

The next step is to wire up the chart to display the sales data we created as a simple bar graph. To do this, create the form's Load event and add the following code:

          public ReportData currentReportData;
  
          public Form1()
          {
              InitializeComponent();
  
              currentReportData = new ReportData();
          }
          
          private void Form1_Load(object sender, EventArgs e)
          {
              chartSales.Series["Series1"].ChartType = SeriesChartType.Bar;
  
  chartSales.Titles.Add(new Title("Sales Data", Docking.Top, new    Font(FontFamily.GenericSansSerif, 18, FontStyle.Bold), Color.Black));
              
  chartSales.Legends.Clear();
  
              chartSales.Series["Series1"].Points.DataBindXY(currentReportData.SalesData, "Name", currentReportData.SalesData, "SalesAmount");
          }

Build and run the application. You will see a simple bar chart, graphically displaying the sales data.



Click here for larger image

Figure 1.1 Sample application with a chart control

Now that we have a working chart viewer, you can see how displaying charts and graphs within an application can add a tremendous amount of value. It helps convey data to the end user without the need for that user to crunch through large amounts of complex data. This is helpful, but it would be more powerful if the user has the ability to take the charts and graphs out of the application in the form of a print report or PDF.

For this, we are going to use the ReportViewer control that comes with Visual Studio. We could use the PrintForm control that is part of the Visual Basic PowerPack (included in VS2008 SP1) to print the chart as well. The drawback to this method is that you end up with a screen print and not a true report.

Before creating the new report form and report, we need to modify the ReportData class. This modification will allow us to store an image of the chart we created and then bind it to the report with the rest of the data. Add the following line to the ReportData class:

  public byte[] SalesChart { get; set; }

Next, create the data sources from the two classes using the Add New Data Source wizard for Objects. Once the data sources are created, we are ready to create our report and set bindings. Add a new Report item that will be displayed using the ReportViewer control. To do so, right-click on the project in Solution Explorer and select Add, New Item. Select the Report template, and name it "reportSales.rdlc." On the report, add two Table elements and one Image element from the Toolbox. In the first table, bind the columns to the SalespersonData data source properties "Name" and "SalesAmount".

In the second table, set the DataSetName property to the ReportData data source. Then place the Image element in the details row of the table. By placing the Image element inside the Table element and setting the DataSetName property we can control which DataSet is associated with the Image element since it does not directly expose this property.

Then, bind the Image item to the "SalesChart" property of the ReportData data source. Be sure to check and configure the data properties of the Image item after binding to the data source. The following properties should be set on the Image item:

  • MIMEType = "image/jpeg"
  • Source = "Database"
  • Value = "=Fields!SalesChart.Value"

When you are finished your report should look similar to the following in design mode.



Click here for larger image

Figure 1.2 The finished report in design view

We will now add a new form ("Form2") to the project, and change the form's text to "Sales Report." This form is going to be used to display the report we just created. From the Toolbox, drag and drop the Microsoft ReportViewer control, found under the Reporting section. Once placed on the form, the control will ask you to specify the report (.rdlc) to use. From the dropdown, select reportSales.rdlc. This will automatically create your binding sources.

The next steps will be to capture the chart image, wire up the forms to pass the data, and bind that data to the report.

On Form1, add a new button and create the click event for the button. In the click event, add the following code to capture the image and store it in the ReportData object.

          private void cmdCreateReport_Click(object sender, EventArgs e)
          {
              using (MemoryStream ms = new MemoryStream())
              {
                  chartSales.SaveImage(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
  
                  currentReportData.SalesChart = ms.ToArray();
              }
  
              Form2 fm2 = new Form2(currentReportData);
              fm2.ShowDialog(this);
          }

You will also notice in the code above that we are passing the ReportData object in the constructor to the new form. To do so, be sure to change Form2's constructor to accept the object as follows:

         private ReportData currentReportData;
  
          public Form2(ReportData reportData)
          {
              InitializeComponent();
  
              currentReportData = reportData;
          }
  The final step is to create the Load event for Form2 and set the report's data sources.  
          private void Form2_Load(object sender, EventArgs e)
          {
              ReportDataBindingSource.DataSource = currentReportData;
              SalespersonDataBindingSource.DataSource = currentReportData.SalesData;
  
              this.reportViewer1.RefreshReport();
          }

Build and run the application. When you click the button on Form1 to generate the report you should see the following, or similar depending on your formatting of the report.



Click here for larger image

Figure 1.3 The finished report as viewed from the application

Conclusion

The Microsoft Chart Controls and Reporting Services offer robust, flexible support for displaying information in your applications. We have shown how to create a simple chart and report. Additionally, we have demonstrated how you can reuse a chart from the application in a report to minimize development effort and for maintainability purposes.

About the Authors

Matt Goebel is a manager with Crowe Horwath LLP in the Indianapolis, Indiana, office. He can be reached at 317.208.2555 or matt.goebel@crowehorwath.com.

Rachel Baker is a senior developer with Crowe Horwath LLP in the Oak Brook, Illinois, office. She can be reached at 630.990.4434 or rachel.baker@crowehorwath.com.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date