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.
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:
<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 ChartLine
s. 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:
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.
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