Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

Monthly Archives: November 2011

Silverlight Gantt Part 3

In my first post, I started with a teaser of what we’re trying to accomplish; in the last post, I show you how to remove the Legend from the chart.

PUTTING A “TODAY” LINE ON THE CHART

The way I handled this was to create a custom class which Inherits the Chart class.

We’ll implement the new “Today” line by (1) adding a custom class; (2) adding a red line (2 px wide Rectangle) to the Control Template of the (now custom) chart; (3) Data Binding the Margin on the Rectangle to put it where it belongs; (4) Data Binding the Tool Tip; and (5) adding code to our codebehind that sets that margin by binding the data to the custom chart.

1)  Add a Custom Class

In your project, add another class file and call it something like GanttChart.cs.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.DataVisualization.Charting;

namespace SilverlightGantt
{
     public class GanttChartChart
   
{
          public string TodaysDateMargin
      

                get { return (string)GetValue(TodaysDateMarginProperty); }
         
set { SetValue(TodaysDateMarginProperty, value); }
     
}

          public static readonly DependencyProperty TodaysDateMarginProperty = DependencyProperty.Register(“TodaysDateMargin”
          
typeof(string), typeof(GanttChart), null);

      public GanttChart()
     
{
          }
     }
}

(2) Add a red line  

Inside your ChartStyle1 template for the Chart (in the UserControl.Resources section), add XAML so that the ChartAreaBorder looks like this [Get the XAML from the last post and add this to it]:

<Border x:Name=”ChartAreaBorder”
     BorderBrush
=”#FF919191″
     BorderThickness
=”1″
     Canvas.ZIndex
=”10″>
    
<Grid DataContext=”{Binding}”
          Margin
=”{Binding ElementName=testChart, Path=TodaysDateMargin}”
          x
:Name=”todaysDateLine”
          HorizontalAlignment
=”Left”
          Grid.Row
=”1″>
         
<Rectangle Fill=”Red”
               Width
=”2″>
              
<ToolTipService.ToolTip>
                   
<TextBlock x:Name=”todaysDateToolTip” Loaded=”todaysDateToolTip_Loaded” />
               </ToolTipService.ToolTip>
         
</Rectangle>
    
</Grid>
</ Border>

We also have to change the  Target Type at the top of the Style and the top of the Control Template to reflect the custom chart’s new type

<Style x:Key=”ChartStyle1″ TargetType=”me:GanttChart”>
    
<Setter Property=”Template”>
         
<Setter.Value>
              
<ControlTemplate TargetType=”me:GanttChart”>

(3) Data Binding the Margin on the Rectangle

If you copied the XAML code from above, you’ve already set the binding, but let’s talk about what this is.  Notice that I have set a DataContext property as well as the Margin property.

 <Grid DataContext=”{Binding}” Margin=”{Binding ElementName=testChart, Path=TodaysDateMargin}”

 To my understanding, the DataContext gets the current context of the DataBinding that happened on the back end.  The binding in the Margin property is giving it a “Parent” data context to look at.  The ElementName is equal to the name of your Chart, and the Path is the Public Property inside that custom Chart class that is used to bind the value. 

(4) Data Binding the Tool Tip

You will notice that in the XAML I provided I have bound the Tooltip not to data, but rather by giving it an Event Handler.  There was something about the way that Tool Tips work that was not allowing me to do a direct {Binding}

<ToolTipService.ToolTip>
    
<TextBlock x:Name=”todaysDateToolTip” Loaded=”todaysDateToolTip_Loaded” />
</ToolTipService.ToolTip>

So we need to implement this method in the Code Behind for the MainPage.xaml.cs file.

private void todaysDateToolTip_Loaded(object sender, RoutedEventArgs e)
{
     TextBlock tip = sender as TextBlock;
  
if (tip != null)
  
{
           tip.Text = DateTime.Today.ToShortDateString();
   
}
}

(5) Set the margin of the rectangle

We’ll use in this code example the “minDate” variable. 

If you recall in my first post I mentioned how I didn’t necessarily like the implementation of the Linear Axis when really you should be using a Date Time Axis.  Basically what I did was I cycled through all of my data points, and I retrieved the lowest date.  I use that as data point “0”.  All of the other dates are a Days difference from that Minimum date.  For example, one year from that date would be the X axis value “365” as that’s how many days difference there are from the minimum date.

So first you get your data, then you cycle through it to find the min and max dates.  This gives you what number the X axis “Stops” at, so you can calculate your “Interval” if you don’t want to use the default one that comes with the Chart (too many that they go on 2 lines).  We’ll cover this code a little more in a later post.

((LinearAxis)(((BarSeries)(mcChart.Series[0])).DependentRangeAxis)).Minimum = 0;
((LinearAxis)(((BarSeries)(mcChart.Series[0])).DependentRangeAxis)).Maximum = max + (max * .1);
((LinearAxis)(((BarSeries)(mcChart.Series[0])).DependentRangeAxis)).Interval = interval;

// Points show from bottom to top, so reverse it to show “1” at the top –
// dataPoints is a List<> of a Custom Class that holds the data you retrieve
dataPoints.Reverse();
((BarSeries)(mcChart.Series[0])).ItemsSource = dataPoints.ToArray();

// First render the chart to get the actual pixel width
testChart.UpdateLayout();

double leftThickness = ((LinearAxis)(((BarSeries)(mcChart.Series[0])).DependentRangeAxis)).
     GetPlotAreaCoordinate((
DateTime.Today.Subtract(minDate)).Days).Value;
testChart.TodaysDateMargin = 
string.Format(“{0},0,0,0”, leftThickness);

What we did here was we used the GetPlotAreaCoordinate from the LinearAxis class, specifying that we’re using the chart’s Dependent Axis – that’s the X axis, the one on the bottom.  Notice though how we first had to call the UpdateLayout() method, as this “renders” the chart and makes it so that the GetPlotAreaCoordinate actually works and doesn’t return 0.

In our next post we will cover adding a Text Label for the end date, and having that label be either inside or outside the box depending on how big the box was.

Silverlight Gantt Part 2

In my first post, I prep you with a teaser of what I’m trying to do with a creating a simplified Gantt Chart.  Now let’s start looking at some of the features I specified in my bulleted list in the first post, as to the tasks we have to accomplish.

GETTING RID OF THE LEGEND

Before we begin, what we’ll need to do is to put 2 more xmlns references in our XAML:

xmlns:dataVisualizationToolkit=”clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit”

xmlns:toolkitChartingPrimitives=”clr-namespace:System.Windows.Controls.DataVisualization.Charting.Primitives;assembly=System.Windows.Controls.DataVisualization.Toolkit”

Now we can create a custom Style with a Control Template in it – basically the control structure that overrides the way it’s laid out by default.  So above your Grid, you want to add a new section called <UserControl.Resources>.  Inside this section, we’ll paste in some code:

<UserControl.Resources>
    
<!– Chart Template Style and Template –>
    
<Style x:Key=”ChartStyle1″ TargetType=”chartingToolkit:Chart”>
         
<Setter Property=”Template”>
              
<Setter.Value>
                   
<ControlTemplate TargetType=”chartingToolkit:Chart”>
                        
<Border BorderBrush=”{TemplateBinding BorderBrush}”
                             
BorderThickness=”{TemplateBinding BorderThickness}”
                              Background
=”{TemplateBinding Background}”
                              Padding
=”{TemplateBinding Padding}”>
                             
<Grid>
                                  
<Grid.RowDefinitions>
                                       
<RowDefinition Height=”Auto”/>
                                       
<RowDefinition Height=”*”/>
                                  
</Grid.RowDefinitions>
                                   
<dataVisualizationToolkit:Title 
                                        Content
=”{TemplateBinding Title}”
                                        Style
=”{TemplateBinding TitleStyle}”/>
                                   
<Grid Margin=”0,15,0,15″ Grid.Row=”1″>
                                       
<Grid.ColumnDefinitions>
                                            
<ColumnDefinition Width=”*”/>
                                             
<ColumnDefinition Width=”Auto”/>
                                        
</Grid.ColumnDefinitions>
                                       
<dataVisualizationToolkit:Legend x:Name=”Legend”
                                             Grid.Column
=”1″ 
                                             Header
=”{TemplateBinding LegendTitle}” 
                                             Style
=”{TemplateBinding LegendStyle}”/>
                                       
<toolkitChartingPrimitives:EdgePanel x:Name=”ChartArea”>
                                            
<Grid Canvas.ZIndex=”-1″/>
                                             
<Border x:Name=”ChartAreaBorder” 
                                                  BorderBrush
=”#FF919191″ 
                                                  BorderThickness
=”1″
                                                  Canvas.ZIndex
=”10″>

                                                                           </Border>
                                        
</toolkitChartingPrimitives:EdgePanel>
                                   
</Grid>
                              
</Grid>
                         
</Border>
                    
</ControlTemplate>
               
</Setter.Value>
          
</Setter>
    
</Style>
</
UserControl.Resources>

Now down below where we have our Chart, we add a new Property underneath Width called Style:

<chartingToolkit:Chart
    
x:Name=”testChart”
    
BorderBrush=”Transparent”
     Height=”400″
    
Width=”400″
    
Style=”{StaticResource ChartStyle1}”>

To get rid of the Legend, all we have to do is comment it out in the Control Template:  dataVisualizationToolkit:Legend (just put <!– before and –> after that whole line to comment it out). 

Next post we’ll talk about how to add the “Today” red line to the chart template (I gave you a little teaser above – in the XAML notice how I have a space inside one of the borders?)

Simple Silverlight Gantt Chart using Toolkit

I had the need to create a VERY simple Gantt chart – with no moving parts – just to show project start and end date ranges, along with a line for today’s date, and throw in some “health” statistics – like below:

Simple Custom Silverlight Gantt Chart using Toolkit

This turned out to have many working pieces, due to the fact that I had to “Template” out a lot of controls (like the chart itself, the series bars, and the axis).

 
Now I want to point out that my implementation is not perfect – there’s a lot that I want to try to improve upon.  For best example, I use a LinearAxis instead of a DateTimeAxis to display my dates, because I could not get the DateTimeAxis to work even for a simple example.
 
But first, let’s list out the pieces that I had to implement individually, starting from a starting point of using a simple run-of-the-mill BarSeries:
  • Getting rid of the Legend
  • Getting a Today Line added to the chart
  • Getting a ToolTip for the Today Line
  • Adding a Text Label for the end point, and having that label be either inside or outside the box depending on how big it was
  • Changing the bar’s ToolTip (this was easy)
  • Getting the X Axis to display Dates instead of “Days from Min Date”  (Messy, yes)
  • Getting the X Axis to have a dynamic interval such that it only shows about 6 -7 points at a time
  • Getting the X Axis to Rotate
  • Getting “Extra Stuff” inside the Y Axis Label area
  • And last but hardest, setting the “Start Date” so that the bars do not go all the way across starting from 0.

PREREQUISITES

The key to the Charts, and apparently Silverlight in General (this was only my second big Silverlight Project) is having controls where their style overwrites their “Control Template”.  Basically this just means that you start with how it is currently laid out and you change it up by adding, removing, and updating the controls.

So the first thing you want to do is to set up your project.  Although I was going to use this in SharePoint for SharePoint data pulling, I just created a vanilla Silverlight Application and said “Yes” to create the sample Web application that lets you debug.  You need to download the Silverlight Toolkit, which gives you TONS of really cool controls, including some basic-to-intermediate charting (http://silverlight.codeplex.com).  You’ll note that it puts the files (by default) in C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Toolkit\Apr10, and from there you have the Bin folder where the dll’s are stored, and in the Source folder are ZIP files with the FULL SOURCE CODE!  There’s also a nifty Samples folder that has one page that shows you a bunch of generated charts.  So grab your references from the BIN folder, and make sure you include System.Windows.Controls.Data.Toolkit, System.Windows.Controls.Toolkit, and System.Windows.Controls.DataVisualization.Toolkit.  I just added them all to be lazy, so I don’t know if I missed telling you about one of them, or if one I listed is redundant for this project.

Once you have your project set up, the first thing you should do is to create a very simple out-of-the-box Bar Chart to see what it looks like.  You’ll need to go to your XAML file and add a reference to the Charting components:

xmlns:chartingToolkit=”clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit”

and if you want some sample data, add a reference to the System dll:

xmlns:sys=”clr-namespace:System;assembly=mscorlib”

 Next, inside your Grid that serves as your Layout Root, “draw” a chart with a Dependent axis (X – bottom horizontal) and an Independent axis (Y – left vertical).  Throw in some sample data, and your chart XAML looks like this:

 <chartingToolkit:Chart
    
x:Name=”testChart”
    
BorderBrush=”Transparent”
     
Height=”400″

     Width=”400″>
    
<chartingToolkit:Chart.Series>
         
<chartingToolkit:BarSeries>
               <chartingToolkit:BarSeries.DependentRangeAxis>
                   
<chartingToolkit:LinearAxis
                        
Orientation=”X” />
               
</chartingToolkit:BarSeries.DependentRangeAxis>
              
<chartingToolkit:BarSeries.IndependentAxis>
                   
<chartingToolkit:CategoryAxis
                        
Orientation=”Y” />
              
</chartingToolkit:BarSeries.IndependentAxis>
              
<chartingToolkit:BarSeries.ItemsSource>
                   
<DoubleCollection>
                        
<sys:Double>50</sys:Double>
                        
<sys:Double>100</sys:Double>
                        
<sys:Double>250</sys:Double>
                   
</DoubleCollection>
              
</chartingToolkit:BarSeries.ItemsSource>
         
</chartingToolkit:BarSeries>
    
</chartingToolkit:Chart.Series>
</chartingToolkit:Chart>

Next post we will start looking into customizing all of these pieces.