A Bar Chart With Multiple Axes and a Legend in WPF

In this post we will create a bar chart with multiple series and two Y-axes. We will do this exclusively in XAML, with the only exception a small code snippet that assigns one of the Y-axis to one of the bar series.

I. Setup of the Project.

We create a new WPF project. The necessary libraries are MindFusion.Charting.Wpf and MindFusion.Licensing. You can add them manually or drag the Bar chart control from the Visual Studio toolbox. In both cases your Window tag should have:

 xmlns:my="clr-namespace:MindFusion.Charting.Wpf;assembly=MindFusion.Charting.Wpf" 

Then we create the bar chart. If you’ve dragged the bar chart control the XAML code has been auto generated.

<my:BarChart BarType="Vertical" Title="Product Statistics" Name="barChart">  
</my:BarChart> 

II. The Axes

The chart has two Y-axes. We create a new AxesCollection and assign it to the YAxes property:

<my:BarChart.YAxes>
     <my:AxesCollection>
         <my:Axis                       
             HorLabelAlignment="Left"
             TitleOffset="10"
             MinValue="0"
             MaxValue="80"
             Tick="2"
             Title ="Cost of Raw Materials"
             TitleRotationAngle="270"
             Interval="8"
             LabelType="AutoScale">
          </my:Axis>
  </my:AxesCollection>
</my:BarChart.YAxes>

Let’s explain the code above line by line. First, the HorLabelAlignment property specifies how the labels will be aligned relative to the axis. If this was an Y2-axis, we would need to set the property to “Right”. Since this is an Y-axis and we want the labels to appear left to the axis, we align them “Let”. The TitleOffset, TitleRotationANgle and Title properties all specify how the label appears – it has an offset of 10 pixels measured from the longest label at the axis (if present). It is rotated so that it doesn’t take much space and its value is “Cost of Raw Materials”.

The MinValue, MaxValue and Interval properties define the axis – it’s start and end values and the span between each pair of adjacent intervals. Tick specifies the length of the ticks – the small lines drawn at each interval point. You can hide them by setting the Tick property to 0.

Finally, the LabelType property. It takes one of the LabelType enumeration values and sets what kind of labels will be drawn at the axis. This Y-axis will show the scale labels.

Then, we add the code for the second Y-axis. We just place the following XAML:

   <my:Axis                       
        HorLabelAlignment="Left"
        Labels="{StaticResource YLabels}"
        TitleOffset="10"
        MinValue="0"
        MaxValue="10"
        Title ="Marketing Costs"
        TitleRotationAngle="270"
        Interval="1"
        Tick="2"
        CustomLabelPosition="AutoScalePoints"
        LabelType="CustomText">
     </my:Axis>   

Most of the properties are the same as with the previous axis. The first difference is the value of the LabelType property. “Custom Text” means we will provide the labels for the axis. We create them as a static resource of strings:

     <x:Array x:Key="YLabels" Type="sys:String">
        <sys:String>0%</sys:String>
        <sys:String>7%</sys:String>
        <sys:String>6.5%</sys:String>
        <sys:String>6.3%</sys:String>
        <sys:String>6.2%</sys:String>
        <sys:String>5.8%</sys:String>
        <sys:String>5.5%</sys:String>
        <sys:String>5.1%</sys:String>
        <sys:String>4.9%</sys:String>
        <sys:String>4.5%</sys:String>
        <sys:String>4.2%</sys:String>
     </x:Array>

Then we assign them to the Labels property. The CustomLabelPosition defines where the labels will be drawn – at the position of the data points of the series, bound to this axis or at the intervals of the auto scale. In this case we render the labels at the auto scale points.

The X-axis is defined similarly:

 <my:BarChart.XAxes>
     <my:AxesCollection>
       <my:Axis
          Labels="{StaticResource XLabels}"
          TitleOffset="10"
          MinValue="0"
          MaxValue="6"
          Title ="Month"
          Interval="1"
          CustomLabelPosition="ChartDataPoints"
          LabelType="CustomText">
       </my:Axis>
      </my:AxesCollection>
   </my:BarChart.XAxes>     

The notable difference here is that we want the labels to appear at the location of chart data. That is why we will need to add a few lines of code later. But now let’s move to the series.

III. The Series

The series in this sample are two. Here is the markup for the first one:

<my:BarSeries XData="1, 2, 3, 4, 5, 6" YData="23, 34, 12, 45, 77, 19"
   Fills="{StaticResource BrushCollection1}" Title="Shoes"
     ToolTipType="ChartData">
     <my:BarSeries.Effect>
           <DropShadowEffect ShadowDepth="3" Opacity="0.7"/>
     </my:BarSeries.Effect>
   </my:BarSeries>            

The series are added directly within the tag, because Series is the default content property of the chart control. The Title, though not visible at the series itself is important for the legend as we will see later. The fills for the series are a collection of brushes, which we have defined as a static resource just like we defined the labels. The data for the series is set with the XData and YData properties. We add a visual effect to make the chart more appealing.

The second series is similar to this one, so we won’t deal with it now.

IV. The Legend

The control supports unlimited number of legends. They are of two types – SeriesLegend and ChartLegend. In our case we use the ChartLegend. This legend defines the legend labels and brushes based on a list of ChartSeries, that were assigned to it. Both types of legends are ItemsControl-s.

<my:BarChart.Legends>
  <my:ChartLegend x:Name="Legend" 
     Background="#FFFAFAFA" 
     my:LayoutPanel.Dock="Bottom"
     Orientation="Horizontal"
     HorizontalAlignment="Center"
     Margin="5"
     CornerRadius="5"                   
     BorderThickness="1" 
     BorderBrush="#FFCECEFF"
     ItemsSource="{Binding ElementName=barChart}">
     <my:ChartLegend.Effect>
          <DropShadowEffect ShadowDepth="3" Opacity="0.7"/>
     </my:ChartLegend.Effect>
   </my:ChartLegend>
 </my:BarChart.Legends>

As you can see, the ItemsSource for the legend is the chart. This means it takes all ChartSeries, which are the default content property of the chart and renders their labels.

V. Binding the X-Axis to a ChartSeries

As you might remember the custom labels at the X-axis are bound to the location of the chart data. This means, we must tell the ChartSeries that it should use exactly the X-axis where we want the labels to appear. This is done with the ChartSeries.XAxis property. We do this in the code behind file.

   if (barChart.XAxes.Count > 0 && barChart.Series.Count > 0)
    {
       Axis xAxis = barChart.XAxes[0];
       BarSeries series = (BarSeries)barChart.Series[0];

       series.XAxis = xAxis;
    }

And this is everything. Here is the final chart:

WPF Chart Control: Bar Chart With Multiple Axes

WPF Chart Control: Bar Chart With Multiple Axes

The sample can be downloaded from here.

Learn more about MindFusion Chart Control for WPF here.