Blog

Using Multiple Visiblox Charts

This post looks at using multiple Visiblox charts within one application, and how to link behaviours and properties between them. Specifically, it will look at binding Zoom behaviours, axis ranges, size, and trackball behaviour.

Binding Zoom Behaviour

There may be occasions where you want to have several charts, all of which react to a zoom on any of the others. This may be especially useful if you have charts stacked one on top of the other, all of which have a common X axis. You might decide, as is done in the Live Streaming Example, to only show the axis on the bottom chart, to maximise the space available to plot data. In this situation it makes sense to zoom all charts whenever another zooms, to ensure the single X axis is still correct for all charts.

This can be demonstrated easily with two charts, and extended to any number you want to link together. First we need to create a couple of charts and add some data, done simply in XAML and code-behind as follows :

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <charts:Chart x:Name="Chart1" Grid.Row="0" LegendVisibility="Collapsed">
        <charts:Chart.Series>
            <charts:LineSeries />
        </charts:Chart.Series>
    </charts:Chart>
    <charts:Chart x:Name="Chart2" Grid.Row="1" LegendVisibility="Collapsed">
        <charts:Chart.Series>
            <charts:LineSeries />
        </charts:Chart.Series>
    </charts:Chart>
</Grid>
public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
        Chart1.Series[0].DataSeries = GenerateDataSeries(10);
        Chart2.Series[0].DataSeries = GenerateDataSeries(20);
    }

    private DataSeries GenerateDataSeries(int seed)
     {
        DataSeries<DateTime, double> dataSeries = new DataSeries<DateTime, double>();
        DateTime _currentDate = new DateTime(2000, 01, 01);
        DateTime _maxDate = new DateTime(2010, 01, 01);
        Random rnd = new Random(seed);
        double value = rnd.NextDouble();

        while (_currentDate < _maxDate)
        {
            dataSeries.Add(new DataPoint<DateTime, double>() 
              { X = _currentDate, Y = value });
            value = rnd.NextDouble();
            _currentDate = _currentDate.AddMonths(1);
        }
        return dataSeries;
     }
}

This gives a basic chart with some data :

Then, we'll add a zoom to Chart1 and link it to Chart2. To add a zoom behaviour to Chart1, we have :

ZoomBehaviour zoom = new ZoomBehaviour();
zoom.ZoomMode = ZoomMode.MouseDrag;
Chart1.Behaviour = zoom;

As each chart Axis has a Zoom property, we can now link the zoom behaviour of Chart1 to Chart2 without needing to define an additional behaviour. We'll do this on both the X and Y axes so that the whole zoom behaviour is mirrored.

Binding XZoomBinding = new Binding("Zoom") { Source = Chart1.XAxis };
((DateTimeAxis)Chart2.XAxis).SetBinding(DateTimeAxis.ZoomProperty, XZoomBinding);

Binding YZoomBinding = new Binding("Zoom") { Source = Chart1.YAxis };
((LinearAxis)Chart2.YAxis).SetBinding(LinearAxis.ZoomProperty, YZoomBinding);

The above code sets up the bindings, and we're done. Any zoom performed on Chart1 will now also occur on Chart2. You can extend this to as many charts as you want, for example with three charts A, B and C, we can just link the zoom of A to B, B to C and C back to A. Adding a zoom behaviour to each of the three charts would then make any zoom on any one of the charts cause the same to happen on all the others - in the current set-up, we can only interact with Chart1, as it's the only one which has a zoom behaviour defined. You can try this by clicking Bind Zoom below, and zooming in on the top chart :

Binding Axis Ranges

As well as linking axis zoom properties, axis ranges can also be bound together in a similar way. Using the same charts previously created, we can bind the X axis ranges together :

Binding XRangeBinding = new Binding("Range") { Source = Chart1.XAxis };
((DateTimeAxis)Chart2.XAxis).SetBinding(DateTimeAxis.RangeProperty, XRangeBinding);

Having set this binding, any change in the X axis range of Chart1 will update Chart2's range accordingly, for example the following will set both our charts' X axis ranges :

DateTimeRange range = (DateTimeRange)Chart1.XAxis.CreateRange();
range.Minimum = new DateTime(2005, 01, 01);
range.Maximum = new DateTime(2008, 01, 01);
Chart1.XAxis.Range = range;

Clicking the Link Range button below will set up this binding, and then change the range of Chart1, which forces Chart2 to also update its range :

Binding Chart Size

Visiblox charts can be also have the properties bound in XAML in the normal way. We can simply add these bindings to our previous chart definitions, for example to bind the size of our charts, we can just modify our XAML to look like this :

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <charts:Chart x:Name="Chart1" Grid.Row="0" LegendVisibility="Collapsed">
        <charts:Chart.Series>
            <charts:LineSeries />
        </charts:Chart.Series>
    </charts:Chart>
    <charts:Chart x:Name="Chart2" Grid.Row="1" LegendVisibility="Collapsed"
                        Height="{Binding ElementName=Chart1, Path=Height, Mode=TwoWay}"
                        Width="{Binding ElementName=Chart1, Path=Width, Mode=TwoWay}">
        <charts:Chart.Series>
            <charts:LineSeries />
        </charts:Chart.Series>
    </charts:Chart>
</Grid>

This two way binding will now update the height and width of either chart when the other chart changes, and any of the previous bindings we've set in code-behind could equally as easily have been done in this way in the XAML. Click on Change Size below to change the size of Chart2, which will also now change the size of Chart1 :

Binding Trackballs

Trackballs are a useful way of interacting with the Visiblox charts, and the data values that the trackballs represent can easily be bound to a TextBlock, for example, to display the information, as is done in the Line Chart example. This trackball behaviour is very simple to enable if you have a single chart showing, however there may be situations where you wish to have multiple charts all updating their trackball position when the mouse is moved over any one of them.

There are several ways that this could be achieved, however probably the most extensible method is to create a new behaviour by extending the BehaviourBase class, which will be responsible for passing information on to other behaviours.

To link our trackball behaviours, we'll add a trackball behaviour to each of our charts, and also add our new behaviour, we'll call a ProxyBehaviour, which will be used as the link. The trackball behaviours can be created in code behind, and we'll add one of them to Chart2 :

TrackballBehaviour trackball1 = new TrackballBehaviour();
TrackballBehaviour trackball2 = new TrackballBehaviour();

Chart2.Behaviour = trackball2;

Now, we'll implement our ProxyBehaviour, and give Chart1 both this and its own trackball behaviour. As the trackball behaviour responds to MouseMove events, this is the only method of BehaviourBase we need to worry about implementing :

public partial class ProxyBehaviour : BehaviourBase
{
    public TrackballBehaviour Trackball { get; set; }

    public ProxyBehaviour() : base("TrackballProxy") { }

    public override void PointerMoved(IBehaviourEventSource sender, PointerEventContext context)
    {
        Trackball.PointerMoved(sender, context);
    }

     protected override void Init() { }

     public override void DeInit() { }
}

This behaviour will take a trackball behaviour, and will call the MouseMove event on that trackball behaviour whenever a mouse move occurs. So, if we add this behaviour to Chart1 and pass it Chart2's trackball behaviour, we can simulate a mouse move on Chart2's behaviour whenever a move occurs on Chart1. To do this we'll need to use a BehaviourManager, with AllowMultipleEnabled set to true :

ProxyBehaviour proxy = new ProxyBehaviour();
proxy.Trackball = trackball2;
proxy.IsEnabled = true;

BehaviourManager manager = new BehaviourManager() { AllowMultipleEnabled = true };
manager.Behaviours.Add(trackball1);
manager.Behaviours.Add(proxy);

Chart1.Behaviour = manager;

So we now have Chart2 with a trackball behaviour, and Chart1 with both a trackball behaviour, and our new ProxyBehaviour which will pass any mouse move calls on to Chart2's trackball behaviour. This has the effect of allowing trackballs to be used on both charts, with any trackball updates on Chart1 being passed on to Chart2. This could easily also be done in reverse giving Chart2 a ProxyBehaviour pointing at Chart1's trackball behaviour, which would pass any trackball movements on either chart on to the other. This method can also be extended to other behaviours, implementing the necessary methods from BehaviourBase and simply passing these calls on to the behaviour you want to link to.

These Trackball and Proxy behaviours will be added to the chart below when Link Trackballs is selected. Once this is done, moving the trackball around the top chart will be mirrored in the bottom chart :

(Edit: As of recent Visiblox versions there is a built-inĀ ProxyBehaviour class, which can be used just like the above - the only difference is that the target is set in a collection called ProxyTargets. The code above has also been updated for behaviour API changes).

Summary

This post has looked at linking various properties of separate instances of Visiblox charts, through XAML and code-behind bindings, and through a new extension of BehaviourBase.

You can download the source code for this example, and to run it you'll need to get a free download of the latest Visiblox release from here.

Comments

Excellent tutorial again, thanks. I want to bind the width of the YAxis, which seems not to work properly.

I have one chart

and want to make sure that the axis of both charts have the same width

But that doesn't work, why?

Thanks for answering.

 
Posted by Martin Weser

sorry, the code was striped out. Basicly i set the width of the axis in my second chart to Width="{Binding ElementName=StockYAxis, Path=Width}" and StockYAxis is the axis of my first chart

 
Posted by Martin Weser

Hi, You simply need to bind to the ActualWidth property on StockYAxis, rather than the Width. The Width property refers to any Width specified directly on StockYAxis whilst ActualWidth is the width the axis ends up occupying after it has been rendered. Hope that helps!

 
Posted by Partha Lal

Is it possible to do trackball binding in a way so it works on both charts? Where moving the mouse on either chart update the trackball on the other chart? What about the same idea, but for zooming? Where a user could zoom in on either chart, and the other chart would zoom to the same location.

Thanks!

 
Posted by Adam

Hi Adam,

Yes that's possible. You'll notice that the way it's implemented here is by adding a proxy behaviour to one of the charts. If you simply mirrored that and pushed the proxying in the other direction as well that would work. You just need to make sure that you don't get into an infinite loop so you need to make sure you only forward the events to the TrackballBehaviour counterpart, not the parent chart (which in turn would send it to the proxy behaviour and back to the original chart). I hope that makes sense.

Jesse

 
Posted by Jesse Beaumont

Hi Jesse,

Thank you for your reply, I was able to get it working how I wanted. One thing I noticed recently though is that the ZoomEnded event is only fired for the chart that I actually performed the zoom on.

Is that how this should work, or am I doing something wrong? If that is how it should work, is there anything I can do to get this event to fire for all charts?

Thanks!

 
Posted by Adam
Hi. Excellent stuff about trackball behaviours. I have one question regarding simple trackball behaviour on a single chart with single or multiple line series. When the chart is first loaded and mouse is hovered over it the ball appears. But after a while the trackball dissapears from the chart and appears after 30-40 seconds back on the chart. I must mention that I tested this on 2 scenarios: one with a history chart that updates itself every 30 seconds and another one with a live chart that updates itself every second. On both scenarios I experience the same behaviour. Do you have any clue about this ? Thanks a lot.
 
Posted by Andrei
Hi Andrei, No that doesn't sound like something we've seen before. Can you send us a project that reproduces the problem on the latest version of Visiblox and we'll be happy to take a look. Thanks Jesse
 
Posted by Jesse

Post a comment