Blog

Using TemplatedSeries with Visiblox Charts: An Example

The ability to template controls in Silverlight and WPF is a powerful feature, representing a separation of concerns between the domain logic and the presentation of that logic; all glued together through data binding. Support for templating and data binding are both included in Visiblox Charts *, and this blog post will demonstrate how to develop an application putting these concepts into use. The application I will build is based upon searching the Twitter public feed for a user-defined search term. Tweets are gathered using the Twitter Search API ( as originally documented by Tim Heuer), analysed for a mood and plotted onto a Visiblox data series.

* The TemplatedSeries and TemplatedLineSeries classes are available in the premium  version of Visiblox Charts.

Tweet Object

Each tweet is represented by a business object of type Tweet:

 public class Tweet
{
    public string AuthorName { get; set; }
    public string AuthorID { get; set; }
    public string Avatar { get;set; }
    public string Message { get; set; }
    public DateTime PublishDate { get; set; }
    public string Mood { get; set; }
}

The application holds an ObservableCollection<Tweet> of Tweet instances, which will be updated with incoming new data. This observable collection will be data-bound to the Visiblox Chart instance using a BindableDataSeries.

BindableDataSeries

The BindableDataSeries class allows for data to be bound directly to a Visiblox Chart. Internally, a BindableDataSeries creates a BindableDataPoint for each of the items in the collection declared as its ItemsSource. The XValueBinding and YValueBindingproperties identify bindings to the properties on a business object that represent the X and Y values for each BindableDataPoint. When setting the ItemsSource property, the DataContext for data points in the BindableDataSeries is your domain model; meaning these bindings can be expressed by property name. For this application, the observable collection of tweets will be the ItemsSource of the BindableDataSeries. The PublishDate and Mood properties of Tweet will be the X and Y values of each BindableDataPoint respectively:

ObservableCollection searchResults = new ObservableCollection();
BindableDataSeries bindableDataSeries = new BindableDataSeries();
bindableDataSeries.ItemsSource = searchResults;
bindableDataSeries.XValueBinding = new Binding() { Path = new PropertyPath("PublishDate") };
bindableDataSeries.YValueBinding = new Binding() { Path = new PropertyPath("Mood") }

NOTE: It is also possible to specify multiple Y values (if you are using a CandlestickSeries, for example) using the YValueBindingsproperty. Now the data is set up, I will attach it to a Visiblox chart series.

TemplatedSeries and TemplatedLineSeries

Visiblox Charts has two templated classes which operate similarly: TemplatedSeries and TemplatedLineSeries. The TemplatedSeries class plots only templated data points. The TemplatedLineSeries class is an extension of LineSeries, adding line drawing in-between templated data points. For this application I will be using the TemplatedSeries class. Both templated classes expose the following dependency properties:

  • The PointTemplate property accepts a Control Template (with a TargetType of TemplatedPoint). This represents the template that is applied to each data point.
  • The SelectedPointTemplate and HighlightedPointTemplate properties allow for templating the "selected" and "highlighted" states of a data point.

In the XAML declaration of my chart, I added a TemplatedSeries:

<charts:Chart x:Name="Chart" Title="TweetsAboutThisTopic">
  <charts:Chart.Series>
    <charts:TemplatedSeries PointTemplate="{StaticResource TimelineSeriesTemplate}" />
  </charts:Chart.Series>
</charts:Chart>

The DataSeries  property of a Visiblox chart series is used to specify what data is to be plotted. So, I assign the BindableDataSeries as the DataSeries of the TemplatedSeries:

Chart.Series[0].DataSeries=bindableDataSeries;

To show the avatar of a Twitter user as a data point, I created a PointTemplate which binds to the Avatarproperty on Tweet (through a converter):

<ControlTemplate TargetType="charts:TemplatedPoint" x:Key="TimelineSeriesTemplate">
  <StackPanel>
    <Border VerticalAlignment="Top" BorderBrush="Black" BorderThickness="1" Background="White">
      <Image Width="40" Height="40"
                 Source="{Binding Path=Avatar, Converter={StaticResource ImageConverter}}" />
    </Border>
  </StackPanel>
</ControlTemplate>

This gives each data point the following look and feel:

The bulk of the chart architecture is now in place! In summary, The ObservableCollection fires out CollectionChanged notifications when new tweets are received. The BindableDataSeries updates its collection of BindableDataPoints accordingly, and displays them on the graph using the PointTemplate. Next, I looked at handling incoming data, and assigning a 'mood' to each tweet.

Incoming Tweets and Evaluating Mood

A Timer controls when to fire each search query, which I have set to thirty seconds. From the resultant data, the last tweet ID is stored to ensure that repeated data is not returned from future search queries. Therefore, it is possible to guarantee the data contains no duplicates, and is in the correct order. The 'mood' of a tweet is evaluated by searching for occurrences of emotional words and emoticons. This is a fairly basic analysis technique, but for the purpose of this example it will suffice! I created a helper class, MoodEvaluator, with a GetMood method which accepts a string and returns another string. Internally it holds a dictionary of positive and negative words, with scores assigned relating to the emotion of the word - so "love" is classed as more positive than "like". The overall score is converted to one of the following categories:

  • Very Positive: A score of 5 or more.
  • Positive: A score between 2 and 4.
  • Neutral: A score between 1 and -1,
  • Negative: A score between -2 and -4.
  • Very Negative: A score of -5 or less.

Visiblox Charts support category axes, although for this example the categories have an explicit order and have a fixed range. The Categories property of CategoryRange accepts a list of IComparables, with each entry representing a fixed category. The categories are plotted according to the order specified, which can be reversed using the SortOrderproperty, should this default behaviour not suit your needs.

Chart.YAxis.Range = new CategoryRange() { Categories = {"Very Negative", "Negative", "Neutral", "Positive", "Very Positive"} };

Having many templated controls in any application can incur a performance cost. This is reduced by enforcing a limit on the size of the ObservableCollection of search results. When the limit is reached, data is removed from the first element. As a further optimisation, the Y axis range is limited, as the bounds are a known constant in this application. Both of these design decisions will optimise the performance of Visiblox Charts.

BusyIndicator Control

I also created an additional Busy Indicator control, which is simply a ContentControl with an additional IsBusy dependency property. For simplicity, I used a T4 template to automatically generate the boilerplate code for the dependency property of this control. The Visual State Manager hides the content declared within the Content property when IsBusy is false. This control is applied overlapping the chart area, and displays in the upper-right corner to inform the user when new tweets are fetched:

Tool Tips

Finally, I added a ToolTipTemplate to the data, so that each tweet and its owner can be shown: Given that this is applied to each point, and the DataContext of each point is a Tweet, it is possible to bind directly to properties on Tweet in the same manner as with PointTemplate! This produces the following outcome on a mouse over:

The chart XAML declaration, therefore, looks like the following (with some styling removed here):

<charts:Chart x:Name="Chart" Title="TweetsAboutThisTopic">
  <charts:Chart.XAxis>
    <charts:DateTimeAxis Title="Time"/>
  </charts:Chart.XAxis>
  <charts:Chart.YAxis>
    <charts:CategoryAxis Title="Mood"/>
  </charts:Chart.YAxis>
  <charts:Chart.Series>
    <charts:TemplatedSeries PointTemplate="{StaticResource TimelineSeriesTemplate}"
                                      ToolTipEnabled="True"
                                      ToolTipPosition="Bottom"
                                      ToolTipTemplate="{StaticResource ToolTipTemplate}"
                                      SelectionMode="None" />
  </charts:Chart.Series>
</charts:Chart>

Outcome

Here's the application!

Due to Silverlight's cross-domain policy, when the application is ran in a web browser the avatars are not loaded. For this reason, the application must be ran in out-of-browser mode. This can be uninstalled from your Control Panel, or from the application itself by right-clicking and selecting "Remove this application".

The source code of this application (without the Visiblox dll) can be downloaded here. Enjoy!

Comments

Thanks a lot for this Sam, it really helped me out when trying to a list of business objects to the visiblox chart I am using!

 
Posted by Chris

Hi,Sam,

I managed to use VisiBlox in a small app. I used MS EF4 & MVVM model.

1. I defined a class like the follwing: public class VmTrace : EntityObject { public DateTime GpsTime {get ; set;} public double Speed {get; set;} public double VmSpd { get; set;} }

2. In the ViewModel, l define an ILIst VmTraces property, something like the following: ... private IList _vmTraces = new List(); public IList VmTraces { get { return _vmTraces; } set { _vmTraces = value; RaisePropertyChanged("VmTraces"); } }

private void GetVmTracesComplete(object sender, EventArgs e) { if(!e.HasError) { VmTraces = e.Results.ToList(); } else .... }

3. In .XAML file, I defined a chart, ... ...

<!-- -->

....

4. when I tried to run the app, I got a Exception: object of type “System.Windows.Data.Binding” can't be converted to object of type “System.Collections.IEnumerable” System.Windows.Markup.XamlParseException: when setting property“Visiblox.Charts.BindableDataSeries.ItemsSource”。 [Line: 107 Position: 64] ---> System.ArgumentException: type “System.Windows.Data.Binding” can't be converted to “System.Collections.IEnumerable”。 located in: System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) located in: System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr) located in: System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) located in: System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) located in: System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) located in: System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) located in: System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index) located in: MS.Internal.XamlMemberInfo.SetValue(Object target, Object value) located in: MS.Internal.XamlManagedRuntimeRPInvokes.SetValue(XamlTypeToken inType, XamlQualifiedObject& inObj, XamlPropertyToken inProperty, XamlQualifiedObject& inValue) located in: System.Windows.Application.LoadComponent(Object component, Uri resourceLocator) located in: VMC.Client.Views.VmaChart.InitializeComponent() located in: VMC.Client.Views.VmaChart..ctor()}

Is the any thing wrong with my program ?

Sincerely, neil

 
Posted by Neil

Hi Neil,

This error is the result of the BindableDataSeries ItemsSource property not being itself bindable. ItemsSource is not a dependency property so does not implement INotifyPropertyChanged. Additionally, the DataContext of a Chart instance does not propagate to the DataContext of a BindableDataSeries instance.

We have recently reviewed the extensibility of Visiblox Charts in an MVVM context, and a fix for this has been implemented. This fix will be released in the next version of Visiblox Charts.

In the meantime, a workaround will be not to bind to the ItemsSource of the chart series, but to bind directly to the IEnumerable itself. It may be necessary to expose this as a property in your view model. ___ Sam

 
Posted by Sam Hogarth

Post a comment