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.