Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Custom Spider or Radar Chart Control

4.96/5 (21 votes)
25 Aug 2012CPOL4 min read 69.6K   2.6K  
An easy to use WPF spider (radar) chart control

Introduction

Although I could find several free charting components on the net, I needed a simple spider or radar chart control. If you don't know what a radar chart is, this is what Wikipedia has to say about them: "A radar chart is a graphical method of displaying multivariate data in the form of a two-dimensional chart of three or more quantitative variables represented on axes starting from the same point. The relative position and angle of the axes is typically uninformative. "

Since it is part of a WPF application, I really didn't want to use any embedded flash charts or ones that use WinForms. As Google did not come to my rescue this time, I decided to roll my own.

Let's start with the end result, so you can decide whether it's worth reading on.

Example of how the WPF spider or radar chart.

Using the Code

To use this chart is very simple. Just include a reference to the WpfCharts assembly, and in your XAML code, you can use it as this:

XML
<WpfCharts:SpiderChart Title="Spider chart"  
                       Lines="{Binding Lines}" 
                       Axis="{Binding Axes}" 
                       Minimum="0" 
                       Maximum="1" 
                       Ticks="5" 
                       ShowLegend="True" 
                       LegendLocation="BottomRight" 
                       LegendBackgroundColor="Aquamarine"/> 

So what do these Dependency Properties mean:

  • Title: Well, if you don't guess what this one does, I hear they are hiring waiters.
  • Lines: An observable collection (if you wish to add lines dynamically, otherwise a list will be fine too) of ChartLines. A ChartLine object holds information about the colors to use, and the data points.
  • Axis: The label that you see at the end of each axis (spoke). Clearly, there should be as many axis as there are points in the chart lines (one point for each spoke). Actually, you need at least three Axis to create a radar chart.
  • Minimum and Maximum value along the axis. In my case, all axis had the same range, so there is only one min and max.
  • Ticks: Represent how many dashed lines (tick marks) you wish to have.
  • ShowLegend, LegendLocation and LegendBackgroundColor should also be clear.

Please note that you can only add lines dynamically, but you cannot add axis (spokes).

As an example of a ChartLine, see the following example, where the line's name, line and fill color, are reused in the legend:

C#
var chartLine = new ChartLine {
    LineColor = Colors.Red,
    FillColor = Color.FromArgb(128, 255, 0, 0),
    LineThickness = 2,
    PointDataSource = new[] { 0.1, 0.2, 0.3, 0.21, 0.6, 0.28, 0.97 },
    Name = "Chart 1"
} 

Points of Interest

I normally try to avoid creating WPF Custom Controls, since re-styling an existing control is much easier. However, in this case, I didn't see an easy way out, so let me briefly explain how I proceeded.

I started with a blank solution, added a WPF Custom Control project (WpfCharts) and a WPF Application (TestSpiderChart), which references WpfCharts. A WPF Custom Control project always contains a UserControl1 class, which I immediately deleted, as well as the style reference to it in the Themes\Generic.xaml file. In WpfCharts, I added a new Custom Control (WPF) named SpiderChart, which automatically added a ControlTemplate (which describes the control's look-and-feel) in Generic.xaml.

The main component of a spider chart is the hub-and-spoke layout. It was easy to find an example of a RadialPanel, which lays out all children in a circle. I used the one from Jobi Joy, but in case you need one, also have a look at this more advanced version by Jeow Li Huan, which fixes several edge cases and is based on Charles Petzold's.

After renaming the RadialPanel to SpiderChartPanel, I added a few Dependency Properties (DP) to set the Minimum, Maximum, Ticks and Lines. BTW, Dr. WPF has published an extensive set of code snippets. Entering dp and pressing tab presents you with an overview of the available DPs. I've added the same DPs and more to the SpiderChart class, and updated the control template in Generic.xaml to use them.

If you have a look at the code, you will see that most of it is pretty straightforward. The actual drawing of the radar chart (background with spokes, tick marks, and lines) is performed by overriding the OnRender method in the SpiderChartPanel. The Axis labels are bound to the ItemsSource DP of the SpiderChartPanel, so we can style and draw them with an ItemTemplate in Generic.xaml.

However, I ran into two minor issues: first, I noticed that my OnRender method in SpiderChartPanel is only called once, before the children collection is set (and therefore not drawing anything). Therefore, I've added a call to InvalidateVisual() in MeasureOverride, so OnRender is called after the children's collection was set. I am pretty sure this is not best practice, and I've tried to get it to work using one of the DP's FrameworkPropertyMetadata options (AffectsRender), but to no avail. Hints anyone?

The other issue involved the fact that I wanted to be able to add Lines dynamically. If the Lines would have been bound to the ItemsSource DP, this would have worked out of the box, but in this case, the Panel's ItemsSource was already occupied by the Axis labels. I therefore needed to create a second DP that behaves as an ItemsSource for the chart lines. This was actually quite easy, as you can see here, and the event handler only needed to call OnRender again to redraw the control. You can check it out by clicking the Add Line button.

C#
// Add handler in case the Lines collection implements INotifyCollectionChanged
var newValueINotifyCollectionChanged = (e.NewValue as IEnumerable) as INotifyCollectionChanged;
if (newValueINotifyCollectionChanged != null)
	newValueINotifyCollectionChanged.CollectionChanged += target.LinesCollectionChanged;

That's all, folks. Happy coding!

History

  • 25th August, 2012: v1 Submission of the article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)