Telerik UI for Windows Phone by Progress

Tooltip Behavior

RadChart provides a tool tip behavior which can be used to visualize arbitrary information related to a data point. The tooltip is triggered by setting the ToolTipTemplate property of ChartToolTipBehavior to a valid DataTemplate object during design-time and holding a finger down somewhere on the chart during run-time. If the user holds down directly over a data point, the tooltip will display information for this particular data point, otherwise it will display information for the closest data point to the hold location.

The user has full control over the visualization of the tooltip itself and over the information that the tooltip will display. The visualization is represented by the ToolTipTemplate but where will the tooltip information come from? What will the data template contents be bound to? In order to define the information that will be visualized the user can provide a custom view model object to which the ToolTipTemplate will be bound. The user can pass an instance of this view model to the behavior and it will be set as the data context for the ToolTipTemplate. This can be achieved via the ContextNeeded event as described in the example below. If the user does not wish to create a custom view model object he/she can use the default context provided by the DefaultContext property in the event arguments.

Using the Tooltip

In this section we will create a custom tool tip that will display the growing or shrinking profits of a fictitious company for each quarter over one year. Even though it will be a bar chart and the bars are more than enough to clearly visualize the relative profits, the example is simple and highlights the usage of RadChart's tooltip API.

First we will need a chart, the XAML for which can look like this:

CopyXAML
<chart:RadCartesianChart x:Name="chart">
   <chart:RadCartesianChart.HorizontalAxis>
      <chart:CategoricalAxis/>
   </chart:RadCartesianChart.HorizontalAxis>

   <chart:RadCartesianChart.VerticalAxis>
       <chart:LinearAxis/>
   </chart:RadCartesianChart.VerticalAxis>

   <chart:BarSeries>
      <chart:BarSeries.PointTemplate>
          <DataTemplate>
              <Rectangle Fill="{StaticResource PhoneForegroundBrush}"/>
          </DataTemplate>
      </chart:BarSeries.PointTemplate>

      <chartEngine:CategoricalDataPoint Category="Q1" Value="4"/>
      <chartEngine:CategoricalDataPoint Category="Q2" Value="4.2"/>
      <chartEngine:CategoricalDataPoint Category="Q3" Value="3.9"/>
      <chartEngine:CategoricalDataPoint Category="Q4" Value="4.5"/>
</chart:BarSeries></chart:RadCartesianChart>

Now we need to define our tooltip behavior and set its tool tip template. It will display the value of the selected data point and also how this value relates to the previous and next quarters.

CopyXAML
<telerikChart:RadCartesianChart.Behaviors>
  <telerikChart:ChartTooltipBehavior />
</telerikChart:RadCartesianChart.Behaviors>

<telerikChart:RadCartesianChart.TooltipTemplate>
  <DataTemplate>
    <Border BorderBrush="Gray" BorderThickness="4" Background="{StaticResource PhoneBackgroundBrush}" Padding="4">
      <StackPanel>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="Profit for " FontWeight="Bold" FontSize="22" />
          <TextBlock Text="{Binding
Path=Quarter}" FontWeight="Bold" FontSize="22" />
          <TextBlock Text=": $" FontWeight="Bold" FontSize="22" />
          <TextBlock Text="{Binding
Path=Profit}" FontWeight="Bold" FontSize="22" />
          <TextBlock Text=" billion" FontWeight="Bold" FontSize="22" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Visibility="{Binding Path=PreviousQuarter, Converter={StaticResource QuarterToVisibilityConverter}}">
          <TextBlock Text="Compared to " />
          <TextBlock Text="{Binding
Path=PreviousQuarter}" />
          <TextBlock Text=": " />
          <TextBlock Text="$" Foreground="{Binding Path=PreviousDifference, Converter={StaticResource ProfitToBrushConverter}}" />
          <TextBlock Text="{Binding
Path=PreviousDifference}" Foreground="{Binding Path=PreviousDifference, Converter={StaticResource ProfitToBrushConverter}}" />
          <TextBlock Text=" billion" Foreground="{Binding Path=PreviousDifference, Converter={StaticResource ProfitToBrushConverter}}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Visibility="{Binding Path=NextQuarter, Converter={StaticResource QuarterToVisibilityConverter}}">
          <TextBlock Text="Compared to " />
          <TextBlock Text="{Binding
Path=NextQuarter}" />
          <TextBlock Text=": " />
          <TextBlock Text="$" Foreground="{Binding Path=NextDifference, Converter={StaticResource ProfitToBrushConverter}}" />
          <TextBlock Text="{Binding
Path=NextDifference}" Foreground="{Binding Path=NextDifference, Converter={StaticResource ProfitToBrushConverter}}" />
          <TextBlock Text=" billion" Foreground="{Binding Path=NextDifference, Converter={StaticResource ProfitToBrushConverter}}" />
        </StackPanel>
      </StackPanel>
    </Border>
  </DataTemplate>
</telerikChart:RadCartesianChart.TooltipTemplate>

Here are the binding converters and their implementations:

CopyXAML
<chart:RadCartesianChart.Resources>
 <local:ProfitToBrushConverter x:Key="ProfitToBrushConverter"/>
 <local:QuarterToVisibilityConverter x:Key="QuarterToVisibilityConverter"/>
</chart:RadCartesianChart.Resources>
CopyVB.NET
Public Class ProfitToBrushConverter
    Implements IValueConverter

    Public Function Convert(value As
        Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        Dim profitDifference As Double = DirectCast(value, Double)
        If (profitDifference < 0) Then
        Return New
        SolidColorBrush(Colors.Red)
        End If

        If (profitDifference > 0) Then
        Return New
        SolidColorBrush(Colors.Green)
        End If

        Return Application.Current.Resources("PhoneForegroundBrush")
    End Function

    Public Function ConvertBack(value As
        Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
        Throw New NotImplementedException
    End Function
End Class

Public Class QuarterToVisibilityConverter
    Implements IValueConverter

    Public Function Convert(value As
        Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        If value Is Nothing
        Then
        Return Visibility.Collapsed
        End If

        Return Visibility.Visible
    End Function

    Public Function ConvertBack(value As
        Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
        Throw New NotImplementedException
    End Function
End Class

Now with the ToolTipTemplate completely defined, we need the data context of the tooltip and a way to tell RadChart to use it. Otherwise the Quarter, Profit, PreviousQuarter, PreviousDifference, NextQuarter and NextDifference properties will not exist and the XAML bindings will fail. Here is the class definition of our custom profit context:

CopyC#
public class ProfitDifferenceContext
{
    public double NextDifference
    {
        get;
        set;
    }

    public object NextQuarter
    {
        get;
        set;
    }

    public double PreviousDifference
    {
        get;
        set;
    }

    public object PreviousQuarter
    {
        get;
        set;
    }

    public double Profit
    {
        get;
        set;
    }

    public object Quarter
    {
        get;
        set;
    }
}
CopyVB.NET
Public Class ProfitDifferenceContext
    Public Property Quarter As Object
    Public Property Profit As Double
    Public Property PreviousQuarter As Object
    Public Property PreviousDifference As Double
    Public Property NextQuarter As Object
    Public Property NextDifference As Double
End Class
And finally we have to construct an object of our profit context and pass it on to RadChart. This is done in the ToolTipContextNeeded event. The event arguments provide a default context that RadChart creates internally. The consumer of the event can use the information in the default context in order to create their custom view model to which the ToolTipTemplate will be bound. The default context provides the following properties: ClosestDataPoint and DataPointInfos. Closest data point is of type DataPointInfo which contains the closest data point to the hold gesture location and the series object to which the point belongs. DataPointInfos provides a collection ofDataPointInfo objects where each object provides the closest data point from each series in the chart. With this default context in hand we create our own profit context like this:
CopyC#
public MainPage()
{
    InitializeComponent();
    ChartTooltipBehavior behavior = (ChartTooltipBehavior)this.chart.Behaviors[0];
    behavior.ContextNeeded += this.ToolTipContextNeeded;
}

private void ToolTipContextNeeded(object sender, Telerik.Windows.Controls.ToolTipContextNeededEventArgs e)
{
    e.Context = this.CreateToolTipContext(e.DefaultContext);
}

private ProfitDifferenceContext CreateToolTipContext(ChartDataContext defaultContext)
{
    CategoricalDataPoint dataPoint = (CategoricalDataPoint)defaultContext.ClosestDataPoint.DataPoint;
    CategoricalSeries series = (CategoricalSeries)defaultContext.ClosestDataPoint.Series;

    int pointIndex = dataPoint.CollectionIndex;
    double prevDiff = 0;
    object previousQuarter = null;
    double nextDiff = 0;
    object nextQuarter = null;
    if(pointIndex > 0)
    {
        CategoricalDataPoint prevPoint = series.DataPoints[pointIndex - 1];
        prevDiff = Math.Round(dataPoint.Value - prevPoint.Value, 4);
        previousQuarter = prevPoint.Category;
    }
    if(pointIndex < series.DataPoints.Count - 1)
    {
        CategoricalDataPoint nextPoint = series.DataPoints[pointIndex + 1];
        nextDiff = Math.Round(dataPoint.Value - nextPoint.Value, 4);
        nextQuarter = nextPoint.Category;
    }
    return new ProfitDifferenceContext()
    {
        Quarter = dataPoint.Category,
        Profit = Math.Round(dataPoint.Value, 4),
        PreviousDifference = prevDiff,
        PreviousQuarter = previousQuarter,
        NextDifference = nextDiff,
        NextQuarter = nextQuarter
    };
}

On the other hand, if we do not assign the profit context to the Context property of the event arguments, the default context will be used as the data context of the ToolTipTemplate.  In this case we will able to display very simple information about the currently tapped data point.