Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Guide to using MSChart for .NET

0.00/5 (No votes)
16 Mar 2010 1  
A guide to using Microsoft's MSChart

Introduction

The accompanying application attempts to demonstrate the use of the MSChart Control in a typical SME Line of Business Application. It covers the use of Pyramid, Pie and Column Graphs as they would typically be used in a business management type application.

Background

Our company specialises in custom Business applications. Basically we develop a generalised application, and then customise that application to meet requirements for specific customers. We rewrite these general apps on a four year cycle, and, this year being a major rewrite year, I was researching and testing various components and technologies that we would likely be using in the upgrades. Previously, we had been using an in-house charting control, but this control has become rather klunky and bloated over the years, and so we wanted to look for an alternative. Commercial Chart controls such as Dundas were way out of budget, and so I came across Microsoft's free Chart control, MSChart for .NET.

Requirements

To run the sample application requires that you have the MSChart Control installed on your machine, which can be downloaded from MSChart. This control requires your installation of VS 2008 to be updated to SP1. It is also better to have the VS Add-On for VS 2008 which can be found at MSChart Add-On For VS 2008. This Add-On requires the .NET Framework to be updated to .NET 3.5 SP1. The database for the application is included with the VS project, you simply need to set the ConString setting in My.Settings to point to the correct file location.

Application Details

As this article is about using the MSChart Control rather than the application as such, I will just give a brief overview of the application, and how it works. The application opens up with a pyramid graph showing the percentage of the company's Gross Sales divided into 6 different product categories. The application defaults, on opening, to the current months figures. On the left are 2 selection panels, and a DataGrid. The top selection panel gives the user the option of selecting which month's figures to view, while the second gives the user the choice of whether to view the categories as a percent of Gross Sales, or as Total Retail Value. The Datagrid shows the actual figures according to the various selections. On the right is a vertical menu which allows the user to Copy the Graph to the clipboard, Save the graph as an image or Print a report showing the graph.

ApplicationViews.png

When the user moves the mouse over one of the graph's Data Points, or Legend Items, the graph will highlight the particular Category. If the user clicks on the selected Data Point, then the graph will change to a Doughnut graph, showing the percentage of each Product within the chosen Category, and the second selection panel will give an added choice of showing Unit Sales, as well as Retail Value and Percent of Sales. The datagrid shows the values for each product in the Category. A sliding trackbar comes into view which enables the user to group products that have a percentage value less than the selected amount. Once again, the graph can be Copied, Saved as image or Printed as a report. A button also becomes visible allowing the user to return to the Category view.

On selecting a product in the pie graph, a second form will open, which will show the user a Column graph showing daily sales for the product over the past 3 full months as well as the current month. On the top left is an information panel showing the various figures for the product, and on the bottom left is a small Column graph showing total unit sales for the product for those months. If the user clicks any month on the small graph, the main graph will change to reflect only that specific month's figures. A button will come into view which will allow the user to reset the main graph to the default. Once again, the user can Copy, Save or Print a report.

ProductSales.png

Basics of the MSChart Control

Once installed on your machine, the chart requires a reference to System.Windows.Forms.DataVisualization in your project. Adding a chart to your form is then just a matter of dragging a chart on to your form from the ToolBox, under Data heading. The control can basically be divided up into 3 sections, The Chart Area, the Title and the Legend. The Title is self explanatory, this being where you let the user know what the graph is all about. The titles can be set in properties by selecting the Titles collection and setting various properties, such as Font, ForeColor for each title in the collection.

Graph Appearance

The main appearance elements of the graph can be set using the properties panel. Using BackColor, BackSecondaryColor and BackHatchStyle, you can come up with some fairly decent, and consistent designs. An interesting property is the BorderSkin property which allows you to define various borders, and set various properties for the border. The main graph in the demo app uses a SkinStyle of FrameTitle1 with a bluish BackColor and a BackGradientStyle of VerticalCentre. One of the most important things I found was to keep properties such as BackGradientStyle consistent throughout the design to get a better look. I found that setting CharttArea.BackColor and Legend.BackColor to Transparent gives the best results. To select the palette used for painting the Data Points, you could use a predefined Palette using the Palette property, or to define your own Palette, you can use the PaletteCustomColor Collection to define various colours, the demo app main graph using a selection of blue shades. If you do define your own Palette then you need to set Palette property to None, otherwise it uses the Palette property as default. There are two properties which can be used to determine the quality of various elements. Text rendering quality can be set with the TextAntiAliasingQualityProperty, and Shadow smoothing can be set using the IsSoftShadows enum.

Graph Features

Series

Series are where it all happens with Chart control. A series consists of a collection of data points. A graph may have an unlimited number of series, and each series can be set to a different chart type (within reason), and can have its appearance properties set separately. The data points in a series can also have their individual appearance properties set, in which case the data point's appearance takes preference over the owning series. Each data point consists of an X value that determines where the data point is placed on the graph, and one or more Y values, dependant on the type of graph. Each type of graph has custom attributes which can be set with the CustomAttributes Property. An example of setting Custom attributes at runtime is shown in the Function GraphSetUp in frmChartExample where we set the Group Threshold Limit by sliding the trackbar on the form.

mscSales.Series(0)("CollectedThreshold") = tbGroupPercentage.Value

There are numerous ways to populate your series, depending on how your data comes to you. The possible data sources include DataView, DataReader, DataSet, DataRow, Arrays, Lists DataAdapters as well as other sources, in fact any object that implements the IEnumerable Interface can be used. In the demo app, I have used two different types of data sources.

In the main graph, I create a list of custom DataPoints. I then iterate through the list manually adding each X and Y value on each iteration.

For intDataPoints As Integer = 0 To lstDataPoints.Count - 1
    mscSales.Series(0).Points.AddXY(lstDataPoints(intDataPoints).DataKey, _
	Format(lstDataPoints(intDataPoints).DataValue, "#,##0.00"))
    Dim strRow() As String = {lstDataPoints(intDataPoints).DataKey, _
	Format(lstDataPoints(intDataPoints).DataValue, "#,##0.00")}
    decGross += lstDataPoints(intDataPoints).DataValue
    dgvDetails.Rows.Add(strRow)
Next

On the second form, I have bound the main graph to a DataTable returned from a function of the Product class. All you need do here is set the DataSource property to the DataTable, and then set your X and Y ValueMembers properties to the correct columns in your DataTable, and call DataBind on your Chart Control, like this:

Dim dtMasterSales As DataTable = prdCurrent.DailySales_
	(dteStartDate, dteEndDate, My.Settings.ConString)
        mscDailySales.DataSource = dtMasterSales
        mscDailySales.Series(0).XValueMember = "Date"
        mscDailySales.Series(0).YValueMembers = "UnitsSold"
        mscDailySales.DataBind()

Graph Interaction

There are many functions that can be used to interact with your graphs, such as Selection, Drill Down, Zooming and Scrolling. There are two main events used to handle these functions, these being the Chart.MouseMove and Chart.MouseDown Event handlers. The use of these functions is very similar, this being to do a HitTest on the mouse position, determining whether the mouse is over a DataPoint, LegendItem, Title, etc., and then performing your actions based on where the user clicked.

Selection

In the demo app, if the user moves over any DataPoint or LegendItem, the points border turns white and thickens. When the user moves away, the point returns to normal.

Private Sub chSales_MouseMove(ByVal sender As Object, _
	ByVal e As System.Windows.Forms.MouseEventArgs) Handles mscSales.MouseMove

        Dim htrResult As HitTestResult = mscSales.HitTest(e.X, e.Y)
        'Go through points setting design elements back to default
        For Each dp As DataPoint In mscSales.Series(0).Points
            dp.BackSecondaryColor = Color.White
            dp.BackHatchStyle = ChartHatchStyle.None
            dp.BorderWidth = 0
        Next dp
        'If users mouse hovers over a datapoint or it's equivalent 
        'Legend Item then set cursor to hand to indicate that it is a link
        'Also we use some design elements to indicate which DataPoint is active
        If htrResult.PointIndex >= 0 Then
            If htrResult.ChartElementType = ChartElementType.DataPoint _
		Or htrResult.ChartElementType = ChartElementType.LegendItem Then
                Me.Cursor = Cursors.Hand
                Dim dpSelected As DataPoint
                dpSelected = mscSales.Series(0).Points(htrResult.PointIndex)
                dpSelected.BackSecondaryColor = Color.Black
                dpSelected.BorderColor = Color.White
                dpSelected.BorderWidth = 1
            End If
        Else
            'Set cursor back to default when leaving selected datapoint
            Me.Cursor = Cursors.Default
        End If

    End Sub

Zooming

Zooming is achieved simply by enabling Zoomable on each axis of a series. This can be done in the designer, or it could be done at runtime.

Chart.ChartAreas(0).AxisX.ScaleView.Zoomable = True

Along with setting up zooming, there are various options to set, such as to determine the position of the scrollbars, once zoomed in.

Chart.ChartAreas(0).AxisX.ScrollBar.IsPositionedInside = True

Drill Down

Drilling down on a graph is achieved by determining which Data Point was selected by the user and making choices depending on the result. In the demo app, a user can select a specific Product Category, and depending on which Category is selected, a new Doughnut Graph will be displayed showing data for the products within that category.

Private Sub chSales_MouseDown(ByVal sender As Object, _
	ByVal e As System.Windows.Forms.MouseEventArgs) Handles mscSales.MouseDown

        Dim htrResult As HitTestResult = mscSales.HitTest(e.X, e.Y)
        If htrResult.ChartElementType = _
	ChartElementType.DataPoint And htrResult.PointIndex >= 0 Then
            SetGraphMonth()
            If gsCurrentStage = GraphStage.Pyramid Then
                gsCurrentStage = GraphStage.Doughnut
                CurrentCategory = lstCategories(htrResult.PointIndex)
                tbGroupPercentage.Value = 3
                rbtnUnitSales.Visible = True
                SetUpGraph()
            Else
                Dim newProductGraph As New ProductSales(CType_
		(lstDataPoints(htrResult.PointIndex).Tag, Product))
                newProductGraph.Show()
            End If
        End If

    End Sub

Tool Tips

Tooltips can be set on various different aspects of the graph, such as Data Points, Legend Items, Titles, etc. These can be setup in the designer, or they can be set during Runtime, by simply detecting which area has been selected, and then setting the ToolTip. This is all done in the Chart.GetToolTip event handler.

Private Sub chSales_GetToolTipText(ByVal sender As Object, _
	ByVal e As System.Windows.Forms.DataVisualization.Charting.ToolTipEventArgs) _
	Handles mscSales.GetToolTipText

        Select Case e.HitTestResult.ChartElementType
            Case ChartElementType.DataPoint
                If e.HitTestResult.PointIndex >= 0 Then
                    Dim strKey As String = lstDataPoints_
			(e.HitTestResult.PointIndex).DataKey
                    Dim strValue As String = Format(lstDataPoints_
			(e.HitTestResult.PointIndex).DataValue, "#,##0.00")
                    e.Text = strKey & ", " & strValue
                End If
        End Select

    End Sub

Printing

The Chart control uses a PrintManager object to manage printing, which is encapsulated in the Chart.Printing property. The PrintManager has several methods that are used for very basic printing operations, such as PageSetup, PrintPreview and Print. These are pretty self explanatory, although it should be said that the Print method only prints a picture of the graph on your paper and nothing else. Although I played around with it a lot, I could not get the Print method to print very well, if such things like borders, etc. were anything except very simple. I found the best way to print reports based on the graph was to set up a PrintPage routine to handle printing, and add a Delegate to handle the specific graphs PrintPage event. In the PrintPage routine, you can design your report as you like.

Private Sub PrintPage(ByVal sender As Object, ByVal ev As PrintPageEventArgs)

        '' Calculate title string position
        Dim titlePosition As New Rectangle(ev.MarginBounds.X, ev.MarginBounds.Y, _
	ev.MarginBounds.Width, ev.MarginBounds.Height)
        Dim stream As New System.IO.MemoryStream() 	' Create a memory stream to 
						' save the chart image    
        CurrentGraph.SaveImage(stream, System.Drawing.Imaging.ImageFormat.Bmp) ' Save the
						' chart image to the stream    
        Dim bmp As New Bitmap(stream) ' Create a bitmap using the stream    
        Dim recPic As New Rectangle(ev.MarginBounds.X, 
			ev.MarginBounds.Y, bmp.Width, bmp.Height)
        Dim fontTitle As New Font("Times New Roman", 16)
        Dim chartTitle As String = String.Empty
        If CurrentGraph.Titles.Count > 0 Then
            chartTitle = CurrentGraph.Titles(0).Text
        End If
        'Dim titleSize As SizeF = ev.Graphics.MeasureString(chartTitle, fontTitle)
        ' titlePosition.Height = CInt(titleSize.Height)

        '' Draw charts title
        'Dim format As New StringFormat()
        'format.Alignment = StringAlignment.Center
        'ev.Graphics.DrawString(chartTitle, fontTitle, 
		Brushes.Black, titlePosition, format)

        '' Calculate first chart position rectangle
        Dim chartPosition As New Rectangle(ev.MarginBounds.X, titlePosition.Bottom, 
		CurrentGraph.Size.Width, CurrentGraph.Size.Height)

        '' Align first chart position on the page
        Dim chartWidthScale As Single = 1 	' CSng(ev.MarginBounds.Width) 
					' / 2.0F / CSng(chartPosition.Width)
        Dim chartHeightScale As Single = 1 	' CSng(ev.MarginBounds.Height) / 
					' CSng(chartPosition.Height)
        chartPosition.Width = CInt(chartPosition.Width * _
			Math.Min(chartWidthScale, chartHeightScale))
        chartPosition.Height = CInt(chartPosition.Height * _
			Math.Min(chartWidthScale, chartHeightScale))

        ' Draw first chart on the printer graphics
        'CurrentGraph.Printing.PrintPaint(ev.Graphics, chartPosition)
        ev.Graphics.DrawImage(bmp, recPic)

End Sub

As you can see, I played around with this section a lot, trying different things.

Saving Graphs to Disk as Images

Your graphs can be saved as various different types of image, such as bitmap, TIFF, JPG, PNG, etc. using the SaveImage method of the Chart control. This method takes two parameters, the filename of the image to be saved, and a ChartImageFormat enum specifying the type of image i.e., BMP, PNG, etc.

Conclusion

This Chart Control seems to be OK to handle most simple, and some quite complicated situations. The use of the control is very simple, and most of the properties and methods all seem pretty self-explanatory, and well named. To get a team up and running with this control would take no more than a few days, really, and the results are quite satisfactory, I think. Considering the expense of the alternatives, or the cost and time factor in building your own control, I would conclude that this is a fairly serious alternative.

History

  • 16th March, 2010: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here