The official Fatica Labs Blog! RSS 2.0
# Wednesday, 25 May 2011

In  the previous sample we shown how to present multiple chart by keeping them aligned in order to be comparable on the horizontal axis by using the WidthSpring feature. But D3 charting can automatically pan and zoom ( both by rect zoom by pressing control and left dragging the mouse on the chart, or by the wheel ) and in this case we loose the axis synchronization. Here below the undesired behavior:

image On the left we see the top chart zoomed, the other ones show the horizontal range thus there is no more relation among the three charts.

So we create an attached property as below:

<ddd:ChartPlotter x:Name="price" ddd:SynchroVisible.Axis="X" ddd:SynchroVisible.With="{Binding ElementName=volume}" …>
 

by applying this property we choose which axis synchronize ( X in the example, but can be Y or XY ) and which chart keep synchronized with, in this case we bind with the chart named “volume”. By repeating the property two chart by two chart:

<ddd:ChartPlotter x:Name="volume" ddd:SynchroVisible.Axis="X" ddd:SynchroVisible.With="{Binding ElementName=ma}" …>
<ddd:ChartPlotter    x:Name="ma"  ddd:SynchroVisible.Axis="X" ddd:SynchroVisible.With="{Binding ElementName=price}" …>

 

so we achieve this interesting result without any code behind:

image This is the result after panning the chart ( by dragging the mouse pointer into the chart area in any of the three chart ) as you can see the other chart properly follow…
image The same apply if we zoom on the chart area ( by pressing ctrl while dragging, or by the mouse wheel ) other chart amplify the resolution on the synchronized axis and the start position accordingly.

Check out the source for this example here.

Wednesday, 25 May 2011 21:49:37 (GMT Daylight Time, UTC+01:00)  #    Comments [5] - Trackback
Charting | D3 | WPF

# Tuesday, 17 May 2011

This post dig a little more into this blog post code about working with and customizing Dynamic Data Display charting. Since the default bar chart implementation does not work, it’s easy to create our own by simply deriving one chart from PointGraphBase.

 

public class BarChart : PointsGraphBase
{

Then we define the classical bounch of dependencies properties which code is not so fancy to see: Thickness, Stroke and fill brush, and so on. The core function we must implement is OnRenderCore. Let’s see how:

protected override void OnRenderCore(System.Windows.Media.DrawingContext dc, RenderState state)
       {
           if (DataSource == null) return;
           var transform = Plotter2D.Viewport.Transform;

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

                   Point zero = new Point(point.X, 0);
                   Point screenPoint = point.DataToScreen(transform);
                   Point screenZero = zero.DataToScreen(transform);

                   double height = screenPoint.Y - screenZero.Y;

                   if (height >= 0)
                   {
                       dc.DrawRectangle(Fill, new Pen(Stroke, StrokeThickness)
, new Rect(screenPoint.X - BarWidth / 2, screenZero.Y, BarWidth, height));
                   }
                   else
                   {
                       dc.DrawRectangle(Fill, new Pen(Stroke, StrokeThickness), 
new Rect(screenPoint.X - BarWidth / 2, screenPoint.Y, BarWidth, -height));
                   }

                   bounds = DataRect.Union(bounds, point);
                   
               }
           }

           Viewport2D.SetContentBounds(this, bounds);
       }
So nothing really special, just some key point:
 
  • ApplyMapping is the function who map the model coordinates into the graphical X-Y coordinates.
  • DataToScreen/ScreenToData are the (extension) method who maps from world coordinates to screen coordinates.
 
The other code is just drawing the rectangles for each point at a certain bar width, and we obtain this:
image
 

 

 

As you can probably see, line are anti-aliased even if we specified SnapToDevicePixels. This is due to the fact that SnapToDevicePixels does not work when we manually draw onto a DrawingContext. If we want to remove this we need to follow these instructions and create some GudelineSet to force WPF align to physical bytes.

Tuesday, 17 May 2011 21:50:48 (GMT Daylight Time, UTC+01:00)  #    Comments [1] - Trackback
Charting | D3 | WPF

# 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