The official Fatica Labs Blog! RSS 2.0
# Sunday, 15 May 2011

I was looking for a good open source charting library and I noticed that is a little difficult to find something really useful and easy to use. I had a look at the WPF Toolkit charting library, a little old for sure, but look promising at a first glance. Unfortunately it is not so easy to use, not brilliant in term of speed and with some discutable behavior,  as for example the absence of a way to remove the chart animation, or removing the fancy bullet in the line chart. By digging a little deeper I found the WPF Dynamic Data Display

imageEven if it seems not recently updated in term of source code, it has a professional level functionality, easy to customize and use, and with some effort MVVM compatible. Make sure to download the unreleased latest source changeset because it is dramatically different from the released one. Here below we show hoe to use this charting library for a very common task, creating multiple chart with the same X Axis. The goal is to have the classical view of a stock chart: Candlestick+Volume some indicators. As a surprise we can see that the latest changeset does not have out of the box any candlestick chart, neither is possible to display a proper bar chart ( there is an OldBarGraph in the code, but it does not seems to work anymore ). Well this is not generally a problem, since a good charting library should be extensible in some way, so we try to create our chart by extending the existing ones. Before to go on let’s have a look on the sample application:

image

The application, even if simple, uses Caliburn Micro, have a look here if you need some notes about that. Data are embedded resources obtained fro YAHOO! Finance for the sole purpose of writing this example application. So lets start with the candles. We note in the source code bounch that exists a MarkerPointsGraph, a graph that display a Marker at each “Y” coordinate, so couldn’t be the candlestick a marker ? Yes, by just deriving from PointMarker. Let see how “difficult” this can be:

 

public class CandleStickPointMarker:ShapePointMarker,ITransformAware
   {
       
       
       public override void Render(System.Windows.Media.DrawingContext dc, Point screenPoint)
       {
           Point screenOpen = GetScreenPoint(Open,screenPoint.X);
           Point screenHigh = GetScreenPoint(High,screenPoint.X);
           Point screenLow = GetScreenPoint(Low, screenPoint.X);
           //screenPoint is the CLOSE by gentleman agreement.
           var close = screenPoint.ScreenToData(Transform).Y;
           Pen strokePen;
           if (Open >= close) // black
           {
               strokePen = new Pen(BlackCandleStroke, CandelstickStrokeWidth);
               var h = -screenOpen.Y + screenPoint.Y;
               dc.DrawRectangle(BlackCandleFill,strokePen 
                   , new Rect(screenPoint.X - CandelstickWidth / 2, screenOpen.Y, CandelstickWidth, h)
                   );
               dc.DrawLine(strokePen, screenLow, screenPoint);
               dc.DrawLine(strokePen, screenHigh, screenOpen);
           }
           else // white
           {
               strokePen=new Pen(WhiteCandleStroke, CandelstickStrokeWidth);
               var h = screenOpen.Y - screenPoint.Y;
               dc.DrawRectangle(WhiteCandleFill, strokePen
                   , new Rect(screenPoint.X - CandelstickWidth / 2, screenPoint.Y, CandelstickWidth, h)
                   );
               dc.DrawLine(strokePen, screenLow, screenOpen);
               dc.DrawLine(strokePen, screenHigh, screenPoint);
           }
       }

       private Point GetScreenPoint(double Open,double screenX)
       {
           Point screen = new Point(0, Open);
           return new Point(screenX,screen.DataToScreen(Transform).Y);
       }
   }

Really easy, we just need a trick: since the marker currently works with a pre-transformed coordinate for the single point the marker usually represent, and we need instead other three value ( Open/Min/Max ) we need to transform these value recovered directly from the data source. This is the reason I had to create the interface ITransformAware , so we can pass the transform to the PointMarker, let see how:

using (IPointEnumerator enumerator = DataSource.GetEnumerator(GetContext()))
            {
                Point point = new Point();
                while (enumerator.MoveNext())
                {
                    enumerator.GetCurrent(ref point);
                    enumerator.ApplyMappings(Marker);

                    //Point screenPoint = point.Transform(state.Visible, state.Output);
                    Point screenPoint = point.DataToScreen(transform);

                    bounds = DataRect.Union(bounds, point);
                    var ta = Marker as ITransformAware;
                    if( null != ta )
                        ta.Transform = transform;
                    Marker.Render(dc, screenPoint);
                }
            }

This is the only trick. DataSOurce side what we have to do is to add the mapping for the missing coordinates:

 

               value.SetYMapping(k => double.Parse(k[4], CultureInfo.InvariantCulture));
               value.AddMapping(CandleStickPointMarker.OpenProperty, k => double.Parse(k[1], CultureInfo.InvariantCulture));
               value.AddMapping(CandleStickPointMarker.HighProperty, k => double.Parse(k[2], CultureInfo.InvariantCulture));
               value.AddMapping(CandleStickPointMarker.LowProperty, k => double.Parse(k[3], CultureInfo.InvariantCulture));

Xaml Side:

<ddd:ChartPlotter x:Name="price"    LegendVisibility="Hidden" NewLegendVisible="False" Grid.Row="1">
            <ddd:VerticalAxisTitle>Price</ddd:VerticalAxisTitle>
            <ddd:WidthSpring SourcePanel="{Binding LeftPanel, ElementName=volume}"/>
            <ddd:MarkerPointsGraph DataSource="{Binding PriceDS}"  >
                <ddd:MarkerPointsGraph.Marker>
                    <ddd:CandleStickPointMarker WhiteCandleFill="Azure" BlackCandleFill="DarkBlue">
                        
                    </ddd:CandleStickPointMarker>
                </ddd:MarkerPointsGraph.Marker>
            </ddd:MarkerPointsGraph>
            <ddd:ChartPlotter.MainHorizontalAxis>
                <ddd:NumericAxis LabelProvider="{StaticResource tickToDate}"/>
            </ddd:ChartPlotter.MainHorizontalAxis>
            
        </ddd:ChartPlotter>

 

Really easy and strightforward. Notice than choosing the candlestick theme does not involve changing a style, bust just setting a brush.  The green line shown one interesting trick, by specifying the WidthSpring, we spring all the chart with the left part aligned. Very important since we need to compare the charts all together. Frankly speaking I did not manage any way to achieve that with WPF Toolkit charts.

imageWidthSpring keeps the left part of the chart aligned.

Bar chart is done by specializing the PointsGraphBase chart. Data are passed as a regular LineChart:

 

public EnumerableDataSource<string[]> VolumeDS
        {
            get
            {
                return volumeDS;
            }
            set
            {
                value.SetXMapping(k => DateTime.ParseExact(k[0], "yyyy-MM-dd", CultureInfo.InvariantCulture).Ticks);
                value.SetYMapping(k => double.Parse(k[5], CultureInfo.InvariantCulture));
                volumeDS = value;
            }
        }

 

An interesting part is what we show as labels on the XAxis ? We can customize that too:

<ddd:NumericAxis LabelProvider="{StaticResource tickToDate}"/>

As we can see, we provide as LabelProvider that produce the label for each tick. If we are not satidfied by the tick, we can provide our TickProvider.

Here below you can find my DynamicDataDisplay version ( sorry for providing that way, I’m tryng to contact the development team to send the patches )

and here the sample app:

Sunday, 15 May 2011 20:31:57 (GMT Daylight Time, UTC+01:00)  #    Comments [1] - Trackback
Charting | D3 | WPF

My Stack Overflow
Contacts

Send mail to the author(s) E-mail

Tags
profile for Felice Pollano at Stack Overflow, Q&A for professional and enthusiast programmers
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2017
Felice Pollano
Sign In
Statistics
Total Posts: 157
This Year: 0
This Month: 0
This Week: 0
Comments: 124
This blog visits
All Content © 2017, Felice Pollano
DasBlog theme 'Business' created by Christoph De Baene (delarou) and modified by Felice Pollano