This example demonstrates the chart interacting with JavaScript. The table below the chart is html that is dynamically generated based on the values in the chart.
Selecting a column on the chart will highlight the corresponding cell in the table and visa versa. This example is simply the Column Chart example with a few extra features to support JavaScript interaction.
You can view the JavaScript code at the bottom of the C# code in comments or by viewing the source of this page. Note the JavaScript code is using the jQuery library.
XAML + CODE +
<UserControl x:Name="Root1" x:Class="Visiblox.Charts.Examples.JavaScript.JavaScriptExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:Visiblox.Charts.Examples.JavaScript"
xmlns:charts="clr-namespace:Visiblox.Charts;assembly=Visiblox.Charts">
<!-- Defining data series and data points in the XAML -->
<!-- Note that these data points and data series could be created in the code behind as well -->
<UserControl.Resources>
<local:GDPDataPointList x:Key="GDPGermany">
<local:GDPDataPoint Date="1990" GDP="1550"/>
<local:GDPDataPoint Date="1995" GDP="2520"/>
<local:GDPDataPoint Date="2000" GDP="1900"/>
<local:GDPDataPoint Date="2005" GDP="2790"/>
</local:GDPDataPointList>
<local:GDPDataPointList x:Key="GDPFrance">
<local:GDPDataPoint Date="1990" GDP="1250"/>
<local:GDPDataPoint Date="1995" GDP="1570"/>
<local:GDPDataPoint Date="2000" GDP="1330"/>
<local:GDPDataPoint Date="2005" GDP="2150"/>
</local:GDPDataPointList>
<local:GDPDataPointList x:Key="GDPUK">
<local:GDPDataPoint Date="1990" GDP="1020"/>
<local:GDPDataPoint Date="1995" GDP="1160"/>
<local:GDPDataPoint Date="2000" GDP="1480"/>
<local:GDPDataPoint Date="2005" GDP="2280"/>
</local:GDPDataPointList>
<local:GDPDataPointList x:Key="GDPCanada">
<local:GDPDataPoint Date="1990" GDP="580"/>
<local:GDPDataPoint Date="1995" GDP="590"/>
<local:GDPDataPoint Date="2000" GDP="720"/>
<local:GDPDataPoint Date="2005" GDP="1130"/>
</local:GDPDataPointList>
<local:GDPDataPointList x:Key="GDPItaly">
<local:GDPDataPoint Date="1990" GDP="1140"/>
<local:GDPDataPoint Date="1995" GDP="1130"/>
<local:GDPDataPoint Date="2000" GDP="1100"/>
<local:GDPDataPoint Date="2005" GDP="1780"/>
</local:GDPDataPointList>
<local:GDPDataPointList x:Key="GDPJapan">
<local:GDPDataPoint Date="1990" GDP="3030"/>
<local:GDPDataPoint Date="1995" GDP="5260"/>
<local:GDPDataPoint Date="2000" GDP="4670"/>
<local:GDPDataPoint Date="2005" GDP="4550"/>
</local:GDPDataPointList>
<local:GDPDataPointList x:Key="GDPUSA">
<local:GDPDataPoint Date="1990" GDP="5800"/>
<local:GDPDataPoint Date="1995" GDP="7400"/>
<local:GDPDataPoint Date="2000" GDP="9950"/>
<local:GDPDataPoint Date="2005" GDP="12640"/>
</local:GDPDataPointList>
<Style x:Key="NoBorder" TargetType="Border">
<Setter Property="BorderThickness" Value="0" />
</Style>
<!-- define tooltip template to show on mouseover of columns -->
<ControlTemplate x:Key="CustomTooltipTemplate">
<Border BorderBrush="Black" BorderThickness="1" Margin="15,0,0,0" >
<Grid Background="LightGray" >
<StackPanel Orientation="Horizontal" >
<TextBlock Text="$" />
<TextBlock Text="{Binding GDP}" />
<TextBlock Margin="2,0" Text="billion" />
</StackPanel>
</Grid>
</Border>
</ControlTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="90*" />
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<!-- Ultimate Trial users should add 'ValidationKey="ENTER TRIAL LICENSE KEY HERE"' to each Chart declaration. -->
<charts:Chart x:Name="MainChart" Width="600" Height="350" Title="G7 Gross Domestic Product" HorizontalAlignment="Center"
PlotAreaBorderStyle="{StaticResource NoBorder}" Grid.Row="0" LegendVisibility="Collapsed">
<!--- Add zoom to the chart -->
<charts:Chart.Behaviour>
<charts:ZoomBehaviour />
</charts:Chart.Behaviour>
<!-- Define Y axis-->
<!-- Note that if no label were to set for the axis this declaration wouldn't be needed, the axis is auto created -->
<charts:Chart.YAxis>
<charts:LinearAxis Title="USD in Billions" ShowMinorTicks="False" LabelFormatString="N0" >
<charts:LinearAxis.Range>
<charts:DoubleRange Minimum="0" Maximum="13000" />
</charts:LinearAxis.Range>
</charts:LinearAxis>
</charts:Chart.YAxis>
<charts:Chart.XAxis>
<charts:CategoryAxis
ShowLabels="True"
ShowGridStripes="False"
ShowMajorGridlines="False"
ShowMajorTicks="False"/>
</charts:Chart.XAxis>
<!-- Defining the 7 column series on the chart-->
<charts:Chart.Series>
<!-- Enable tooltips and allow user to select entire series by clicking on one point -->
<charts:ColumnSeries ToolTipEnabled="True" ToolTipTemplate="{StaticResource CustomTooltipTemplate}" SelectionMode="SinglePoint">
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="USA" ItemsSource="{StaticResource GDPUSA}" XValueBinding="{Binding Path=Date}" YValueBinding="{Binding Path=GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries ToolTipEnabled="True" ToolTipTemplate="{StaticResource CustomTooltipTemplate}" SelectionMode="SinglePoint">
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="Japan" ItemsSource="{StaticResource GDPJapan}" XValueBinding="{Binding Path=Date}" YValueBinding="{Binding Path=GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries ToolTipEnabled="True" ToolTipTemplate="{StaticResource CustomTooltipTemplate}" SelectionMode="SinglePoint">
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="Germany" ItemsSource="{StaticResource GDPGermany}" XValueBinding="{Binding Path=Date}" YValueBinding="{Binding Path=GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries ToolTipEnabled="True" ToolTipTemplate="{StaticResource CustomTooltipTemplate}" SelectionMode="SinglePoint">
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="France" ItemsSource="{StaticResource GDPFrance}" XValueBinding="{Binding Path=Date}" YValueBinding="{Binding Path=GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries ToolTipEnabled="True" ToolTipTemplate="{StaticResource CustomTooltipTemplate}" SelectionMode="SinglePoint">
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="UK" ItemsSource="{StaticResource GDPUK}" XValueBinding="{Binding Path=Date}" YValueBinding="{Binding Path=GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries ToolTipEnabled="True" ToolTipTemplate="{StaticResource CustomTooltipTemplate}" SelectionMode="SinglePoint">
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="Italy" ItemsSource="{StaticResource GDPItaly}" XValueBinding="{Binding Path=Date}" YValueBinding="{Binding Path=GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries ToolTipEnabled="True" ToolTipTemplate="{StaticResource CustomTooltipTemplate}" SelectionMode="SinglePoint">
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="Canada" ItemsSource="{StaticResource GDPCanada}" XValueBinding="{Binding Path=Date}" YValueBinding="{Binding Path=GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
</charts:Chart.Series>
</charts:Chart>
<!-- Define coloured labels under the chart -->
<StackPanel x:Name="CustomLegend" Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Center" Margin="0,0,-30,15">
<Rectangle Margin="0,0,2,0" Height="10" Width="10" Fill="{Binding ElementName=MainChart, Path=Series[0].PointFill}" VerticalAlignment="Center" />
<TextBlock Margin="0,0,8,0" Text="{Binding ElementName=MainChart, Path=Series[0].DataSeries.Title}" VerticalAlignment="Center"/>
<Rectangle Margin="5,0,2,0" Height="10" Width="10" Fill="{Binding ElementName=MainChart, Path=Series[1].PointFill}" VerticalAlignment="Center" />
<TextBlock Margin="0,0,8,0" Text="{Binding ElementName=MainChart, Path=Series[1].DataSeries.Title}" VerticalAlignment="Center"/>
<Rectangle Margin="5,0,2,0" Height="10" Width="10" Fill="{Binding ElementName=MainChart, Path=Series[2].PointFill}" VerticalAlignment="Center" />
<TextBlock Margin="0,0,8,0" Text="{Binding ElementName=MainChart, Path=Series[2].DataSeries.Title}" VerticalAlignment="Center"/>
<Rectangle Margin="5,0,2,0" Height="10" Width="10" Fill="{Binding ElementName=MainChart, Path=Series[3].PointFill}" VerticalAlignment="Center" />
<TextBlock Margin="0,0,8,0" Text="{Binding ElementName=MainChart, Path=Series[3].DataSeries.Title}" VerticalAlignment="Center"/>
<Rectangle Margin="5,0,2,0" Height="10" Width="10" Fill="{Binding ElementName=MainChart, Path=Series[4].PointFill}" VerticalAlignment="Center" />
<TextBlock Margin="0,0,8,0" Text="{Binding ElementName=MainChart, Path=Series[4].DataSeries.Title}" VerticalAlignment="Center"/>
<Rectangle Margin="5,0,2,0" Height="10" Width="10" Fill="{Binding ElementName=MainChart, Path=Series[5].PointFill}" VerticalAlignment="Center" />
<TextBlock Margin="0,0,8,0" Text="{Binding ElementName=MainChart, Path=Series[5].DataSeries.Title}" VerticalAlignment="Center"/>
<Rectangle Margin="5,0,2,0" Height="10" Width="10" Fill="{Binding ElementName=MainChart, Path=Series[6].PointFill}" VerticalAlignment="Center" />
<TextBlock Margin="0,0,8,0" Text="{Binding ElementName=MainChart, Path=Series[6].DataSeries.Title}" VerticalAlignment="Center"/>
</StackPanel>
</Grid>
</UserControl>
^ Back To Top
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Input;
namespace Visiblox.Charts.Examples.JavaScript
{
/// <summary>
/// A simple chart example displaying bar charts and interacting with JavaScript.
/// </summary>
[ScriptableType]
public partial class JavaScriptExample : UserControl
{
private string selectionChangedComboBox;
private string dataChangedComboBox;
private bool selectEventActive = true;
/// <summary>
/// Construct the example control.
/// </summary>
public JavaScriptExample()
{
InitializeComponent();
//Fix HighlightedStyle to Normal style and add events on series.
foreach (ColumnSeries series in MainChart.Series)
{
series.HighlightedStyle = series.NormalStyle;
series.MouseEnter += new MouseEventHandler(Series_MouseEnter);
series.MouseLeave += new MouseEventHandler(Series_MouseLeave);
series.SelectionChanged += new SelectionChangedEventHandler(Series_SelectionChanged);
series.DataSeriesCollectionChanged += new EventHandler<EventArgs>(Series_DataSeriesCollectionChanged);
}
Loaded += new RoutedEventHandler(JavaScriptExample_Loaded);
}
/// <summary>
/// Called when the control has completed loading.
/// </summary>
void JavaScriptExample_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.RegisterScriptableObject("chart", this);
}
/// <summary>
/// Called when the data in one of the series changes.
/// </summary>
void Series_DataSeriesCollectionChanged(object sender, EventArgs e)
{
UpdateJs();
}
/// <summary>
/// Called when the selected point on a series changes.
/// </summary>
void Series_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (selectEventActive)
{
//prevents us from infinitely triggering select events
selectEventActive = false;
try
{
IChartSingleSeries series = sender as IChartSingleSeries;
// unselect all other series.
UnselectAllButGiven(series);
// pass selection event onto javascript.
if (selectionChangedComboBox != null)
{
GDPDataPoint selected = series.SelectedItem as GDPDataPoint;
object[] jsArgs = new object[2];
jsArgs[0] = series.DataSeries.Title;
jsArgs[1] = selected;
HtmlPage.Window.Invoke(selectionChangedComboBox, jsArgs);
}
}
finally
{
// always reset this.
selectEventActive = true;
}
}
}
/// <summary>
/// Called when the mouse enters a series area.
/// </summary>
void Series_MouseEnter(object sender, MouseEventArgs e)
{
this.Cursor = Cursors.Hand;
}
/// <summary>
/// Called when the mouse exits a series area.
/// </summary>
void Series_MouseLeave(object sender, MouseEventArgs e)
{
this.Cursor = Cursors.Arrow;
}
/// <summary>
/// Exposed to the JavaScript.
/// Registers a JavaScript method to be called when the selection changes.
/// </summary>
/// <param name="jsMethodName">The name of the JavaScript method to be called.</param>
[ScriptableMember]
public void RegisterSelectionCb(string jsMethodName)
{
selectionChangedComboBox = jsMethodName;
}
/// <summary>
/// Exposed to the JavaScript.
/// Registers a JavaScript method to be called when the chart data changes.
/// This will immediately call back to the JavaScript with the current data.
/// </summary>
/// <param name="jsMethodName">The name of the JavaScript method to be called.</param>
[ScriptableMember]
public void RegisterDataChangedCb(string jsMethodName)
{
dataChangedComboBox = jsMethodName;
// call immediately on register to update the js with the current data set.
UpdateJs();
}
/// <summary>
/// Exposed to the JavaScript.
/// This will update the selection in the chart. Note this will trigger
/// the charts selection changed event so the JavaScript will get a callback indicating that the
/// selection has changed.
/// If one of the parameters is null this will clear the current selection.
/// </summary>
/// <param name="seriesTitle">The series title of the new selection or null for no new selection.</param>
/// <param name="date">The date of the new selection or null for no new selection.</param>
[ScriptableMember]
public void ChangeSelection(string seriesTitle, string date)
{
// clear all selections.
UnselectAllButGiven(null);
// catch deselect case first
if (seriesTitle == null || date == null)
{
return;
}
IChartSingleSeries series = MainChart.Series.Where(p => p.DataSeries.Title.Equals(seriesTitle)).FirstOrDefault() as IChartSingleSeries;
if (series == null)
return;
// now find the point.
foreach (GDPDataPoint point in (series.DataSeries as BindableDataSeries).ItemsSource)
{
if (date.Equals(point.Date))
{
series.SelectedItem = point;
break;
}
}
}
/// <summary>
/// Clears all selections except from the one on the given series.
/// If the given series is null clears all selections.
/// </summary>
private void UnselectAllButGiven(IChartSeries series)
{
foreach (IChartSingleSeries chartSeries in MainChart.Series)
{
if (series == null || !series.Equals(chartSeries))
{
chartSeries.SelectedItems.Clear();
}
}
}
/// <summary>
/// Update the JavaScript with the latest data model.
/// </summary>
private void UpdateJs()
{
if (dataChangedComboBox != null)
{
// Build an object suitable for js.
// We don't want to send the whole series object over here so extract what we need into a new structure.
List<JSDataSeries> data = new List<JSDataSeries>(MainChart.Series.Count);
foreach (IChartSeries series in MainChart.Series)
{
GDPDataPointList pointList = new GDPDataPointList();
pointList.AddRange((series.DataSeries as BindableDataSeries).ItemsSource as IEnumerable<GDPDataPoint>);
data.Add(new JSDataSeries() { Title = series.DataSeries.Title, Points = pointList });
}
HtmlPage.Window.Invoke(dataChangedComboBox, data);
}
}
}
// Data model
/// <summary>
/// List of GDPDataPoints.
/// </summary>
public class GDPDataPointList : List<GDPDataPoint> { }
/// <summary>
/// Class representing a GPD data point.
/// </summary>
[ScriptableType]
public class GDPDataPoint
{
/// <summary>
/// The year, as a string, that this GDP data point aligns to
/// </summary>
[ScriptableMember]
public string Date { get; set; }
/// <summary>
/// The GDP value for this date
/// </summary>
[ScriptableMember]
public double GDP { get; set; }
}
/// <summary>
/// Class representing a data series.
/// </summary>
[ScriptableType]
public class JSDataSeries
{
[ScriptableMember]
public string Title { get; set; }
[ScriptableMember]
public List<GDPDataPoint> Points { get; set; }
}
}
// The HTML/JavaScript code for this example is included in the comments below.
//[script type="text/javascript"]
// // NOTE this uses and requires jQuery >= 1.4.2. See http://jquery.com/ for more info.
// /**
// * Returns the column index for the given column title.
// * @param colTitle the title of the column to find.
// * @return the column index or -1.
// */
// function getColIndex(colTitle) {
// var titleRow, result;
// titleRow = $("#chartTable").find("tr").first();
// result = -1;
// // Loop through each th in the first row in the table.
// titleRow.children("th").each(function (idx, th) {
// if (colTitle === $(th).html()) {
// result = idx;
// return false; // exit each loop
// }
// });
// return result;
// }
// /**
// * Returns the row index for the given row title.
// * @param rowTitle the title of the row to find.
// * @return the row index or -1.
// */
// function getRowIndex(rowTitle) {
// var rows, result;
// rows = $("#chartTable").find("tr");
// result = -1;
// // for each row check the value of the first th cell.
// rows.each(function (idx, tr) {
// if (rowTitle === $(tr).children("th").first().html()) {
// result = idx;
// return false; // exit each loop
// }
// });
// return result;
// }
// /**
// * Called by the chart when the selection changes.
// * @param seriesName the name of the series that is selected or null.
// * @param gdpPoint the point object that is selected in the chart.
// */
// function chartSelectionCallback(seriesName, gdpPoint) {
// var colIdx, rowIdx, tr, td;
// // deselect any existing selections first
// $("#chartTable").find("td").removeClass("exampleTableHighlight");
// if (gdpPoint) {
// colIdx = getColIndex(gdpPoint.Date);
// rowIdx = getRowIndex(seriesName);
// tr = $("#chartTable").find("tr")[rowIdx];
// // -1 as we want to ignore the title th column that will be filtered for us.
// td = $(tr).children("td")[colIdx - 1];
// $(td).addClass("exampleTableHighlight");
// }
// // else was a deselection
// }
// /**
// * Find the row title for a given td object.
// * @param td the object to get the row title for.
// * @return the row title.
// */
// function getRowTitle(td) {
// return $(td).parent("tr").children("th").first().html();
// }
// /**
// * Find the column title for a given td object.
// * @param td the object to get the column title for.
// * @return the column title.
// */
// function getColTitle(td) {
// var colIndex, titleRow, colHeading;
// colIndex = $(td).index(); // the index in the parent tr.
// titleRow = $("#chartTable").find("tr").first();
// colHeading = titleRow.children("th").get(colIndex);
// return $(colHeading).html();
// }
// /**
// * Called when a user clicks on a cell in the table.
// */
// function chartCellClicked() {
// var chartControl;
// chartControl = $("#control").children("object")[0];
// if ($(this).hasClass("exampleTableHighlight")) {
// // if we are selected undo selection
// chartControl.Content.chart.ChangeSelection(null, null);
// } else {
// // Make the selection.
// // No need to set the selected css class.
// // This will be done when the chart calls back in with a selection change.
// chartControl.Content.chart.ChangeSelection(getRowTitle(this), getColTitle(this));
// }
// return false;
// }
// /**
// * Called by the chart control when the data updates.
// * This rebuilds the whole table.
// * @param series The series that the table should contain.
// */
// function chartUpdateDataCallback(series) {
// var tr, th, td, seriesI, pointI, currSeries, currPoint, a;
// //clear out any existing data.
// $("#chartTable").children("tbody").empty();
// // catch empty case
// if (!series || series.length === 0) {
// return;
// }
// // Date heading row. For now we will assume all series will be the same length and contain the same dates.
// // so just use the first one to get the dates from.
// tr = $("<tr></tr>");
// th = $("<th></th>");
// tr.append(th);
// for (pointI = 0; pointI < series[0].Points.length; pointI++) {
// currPoint = series[0].Points[pointI];
// th = $("<th></th>");
// th.html(currPoint.Date);
// tr.append(th);
// }
// $("#chartTable").children("tbody").append(tr);
// // now append all data
// for (seriesI = 0; seriesI < series.length; seriesI++) {
// currSeries = series[seriesI];
// // Row title.
// tr = $("<tr></tr>");
// th = $("<th></th>").html(currSeries.Title);
// tr.append(th);
// // Columns
// for (pointI = 0; pointI < currSeries.Points.length; pointI++) {
// currPoint = currSeries.Points[pointI];
// td = $("<td></td>");
// a = $("<a></a>").attr("href", "#");
// a.html(currPoint.GDP);
// td.append(a);
// td.click(chartCellClicked);
// tr.append(td);
// }
// $("#chartTable").children("tbody").append(tr);
// }
// }
// /**
// * Called when the silverlight control loads.
// * This registers the callbacks with the chart.
// */
// function chartOnLoad() {
// var chartControl = $("#control").children("object")[0];
// chartControl.Content.chart.RegisterDataChangedCb("chartUpdateDataCallback");
// chartControl.Content.chart.RegisterSelectionCb("chartSelectionCallback");
// }
//[/script]
//<div class="exampleControlHTML">
// <table id="chartTable" class="exampleTable"><tbody></tbody></table>
//</div>
^ Back To Top

