Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

Category Archives: Silverlight

Silverlight Gantt Part 6

At long last I’m getting back to my posts with the Silverlight Gantt Chart.  I had a request to post the source code, and if I can clean it properly, I will do so – probably in my next (final) post.

Getting the X Axis to have a dynamic interval such that it only shows about 6 -7 points at a time

By default, it shows so many points on the X axis that your points either run into each other or get put on 2 lines.  So this is a way to control how many points get shown.

This one’s easy – you just need to calculate a numerical value and set the interval in code.  I’m sure my math isn’t perfect, but this was my quick n dirty way to calculate and then set the interval .

// Set the Interval so we don’t get 2 rows of labels, which 
// seems to be the default
int maxNumOfLabelValues = 4;
int interval = 1;

if(max > 10000)
{
     interval = Convert.ToInt32((Math.Round((max / 10000)) * 10000) / maxNumOfLabelValues);
}
elseif(max > 1000)
{
     interval = Convert.ToInt32((Math.Round((max / 1000)) * 1000) / maxNumOfLabelValues);
}
elseif(max > 100)
{
     interval = Convert.ToInt32((Math.Round((max / 100)) * 100) / maxNumOfLabelValues);
}
elseif(max > 10)
{
     interval = Convert.ToInt32((Math.Round((max / 10)) * 10) / maxNumOfLabelValues);
}

// Set the axis data
((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;

 Getting the X Axis to Rotate

I wanted to show the X Axis point labels in such a way that they didn’t overlap on top of one another.  So we apply a transform to the XAML to rotate the labels by a negative 60 degrees.

<!– X Axis –>
<Style x:Key=”HorizontalAxisStyle” TargetType=”chartingToolkit:AxisLabel”>
     <Setter Property=”Margin” Value=”0,5,0,0″/>
     <Setter Property=”Template”>
          <Setter.Value>
               <ControlTemplate TargetType=”chartingToolkit:AxisLabel”>
                    <layout:LayoutTransformer>
                         <layout:LayoutTransformer.LayoutTransform>
                              <RotateTransform Angle=”-60″ />
                         </layout:LayoutTransformer.LayoutTransform>
                         <TextBlock Padding=”0″ DataContext=”{Binding}”
                              Text
=”{Binding Converter={StaticResource AxisDateFormatConverter}, ConverterParameter={StaticResource axisMinDate}}” />
                    </layout:LayoutTransformer>
               </ControlTemplate>
          </Setter.Value>
     </Setter>
</Style>

 Getting “Extra Stuff” inside the Y Axis Label area

I wanted to put some “extra” items inside the Y axis label – not just the label of the data point, but also some extra information about that data point.  So for example I wanted to put a glassy ball-dot kind of thing, representing the “health” of the project, as well as putting the project name, as well as putting some blurb about the status of the project.

The first thing I had to do was to create a Data Item Object (did I show you this object in an earlier post?) and then a custom class for the Gantt Y Axis object.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace SilverlightGantt
{
     public class DataItem
        {
          public string Key { get; set; }
          public DateTime MinDate { get; set; }
          public double StartDate { get; set; }
          publicd ouble EndDate { get; set; }
          public string TextLabelMargin { get; set; }
          public string Foreground { get; set; }
          
public string DotBackground { get; set; }
          public string LabelText { get; set; }
          public string ExtraStuffVisibility { get; set}

          public DateTime StartDateDate { get; set; }
          public DateTime EndDateDate { get; set; }

          public string FormattedValue
          {
               get
                         {
                                   return string.Format(“{0:MM/dd/yyyy}”, EndDateDate);
              
}
                }

               public void SetDateDoubles()
         
{
                        if (MinDate != DateTime.MinValue)
              
{
                                 StartDate = (StartDateDate.Subtract(MinDate)).Days;
                                 EndDate = (EndDateDate.Subtract(MinDate)).Days;
                        }
               }

        public DataItem(string _key, DateTime _start, DateTime _end, string _margin, string _foreground)
        
{
                      Key = _key;
                      StartDateDate = _start;
                      EndDateDate = _end;
                      Foreground = _foreground;
             }

        public DataItem()
       
{
             }
       }
}

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

namespace SilverlightGantt
{
     [StyleTypedProperty(Property = “AxisLabelStyle”, StyleTargetType = typeof(AxisLabel))]

     public class GanttYAxisCategoryAxis
        {
          public GanttYAxis()
                {
                }

          protected
overridevoid PrepareAxisLabel(Control label, object dataContext)
                {
               object newCTX = null;

               object ctx = this.DataContext;
               DataItem[] allItems = ctx as DataItem[];
               if (allItems != null)

                        {
                   foreach (DataItem item in allItems)

                               {
                                       if(dataContext.ToString() == item.Key)
                                       {
                                                 newCTX = item;
                             break;
                                      }
                              }
                      }

             base.PrepareAxisLabel(label, newCTX);
               }
       }
}

So the next thing I had to do was to put everything in the Control Template for the Y Axis that I wanted to see, and then add binding to be able to fill in that information.

<!– Y Axis –>
<Style x:Key=”VerticalAxisStyle” TargetType=”chartingToolkit:AxisLabel”>
     <Setter Property=”Margin” Value=”0,0,5,0″/>
     <Setter Property=”HorizontalAlignment” Value=”Left” />
     <Setter Property=”Template”>
          <Setter.Value>
               <ControlTemplate TargetType=”chartingToolkit:AxisLabel”>
                    <StackPanel x:Name=”xAxisStackPanel” MinHeight=”37″ Orientation=”Horizontal” HorizontalAlignment=”Left” Margin=”0,0,5,0″>
                         <!– Project Name : Key –>
                         <TextBlock Visibility=”{Binding ExtraStuffVisibility}” Text=”{Binding Key}” TextWrapping=”Wrap” Width=”75″ 
                              HorizontalAlignment
=”Left” VerticalAlignment=”Center” />
                         <!– Glassy Dot : DotBackground –>
                         <Grid Margin=”5,0,0,0″ Visibility=”{Binding ExtraStuffVisibility}”>
                              <Ellipse Fill=”{Binding DotBackground}” HorizontalAlignment=”Left” Stroke=”Black” Width=”25″ Height=”25″/>
                              <Ellipse HorizontalAlignment=”Left” StrokeThickness=”1″ Stroke=”{Binding DotBackground}” Width=”25″ Height=”25″ Opacity=”0.5″>
                                   <Ellipse.Fill>
                                        <LinearGradientBrush EndPoint=”0.5,1″ StartPoint=”0.5,0″>
                                             <GradientStop Color=”#3FFFFFFF”/>
                                             <GradientStop Color=”#00787878″ Offset=”0.5″/>
                                             <GradientStop Color=”#99000000″ Offset=”1″/>
                                        </LinearGradientBrush>
                                   </Ellipse.Fill>
                              </Ellipse>
                              <Ellipse HorizontalAlignment=”Left” StrokeThickness=”0″ Stroke=”{Binding DotBackground}” Width=”25″ Height=”25″>
                                   <Ellipse.Fill>
                                        <RadialGradientBrush>
                                             <GradientStop Color=”#99000000″ Offset=”1″/>
                                             <GradientStop Color=”#66FFFFFF”/>
                                        </RadialGradientBrush>
                                   </Ellipse.Fill>
                              </Ellipse>
                              <Ellipse HorizontalAlignment=”Left” Margin=”5,2,0,0″ VerticalAlignment=”Top” Width=”15″ Height=”15″>
                                   <Ellipse.Fill>
                                        <LinearGradientBrush EndPoint=”0.5,1″ StartPoint=”0.5,0″>
                                             <GradientStop Color=”Transparent” Offset=”1″/>
                                             <GradientStop Color=”#BAFFFFFF”/>
                                        </LinearGradientBrush>
                                   </Ellipse.Fill>
                              </Ellipse>
                         </Grid>
                         <!– Status Message : LabelText –>
                         <TextBlock Margin=”5,0,0,0″ Visibility=”{Binding ExtraStuffVisibility}” TextWrapping=”Wrap” Text=”{Binding LabelText}”
                              Width
=”100″ Height=”Auto”HorizontalAlignment=”Left” VerticalAlignment=”Center” />
                    </StackPanel>
               </ControlTemplate>
          </Setter.Value>
     </Setter>
</Style>

Then I needed to update my Grid XAML to reference the custom Gantt Y Axis object:

<Grid x:Name=”LayoutRoot” Background=”White”>
     <me:GanttChart x:Name=”mcChart” Margin=”0″ BorderBrush=”Transparent” Height=”400″ Width=”500″
          Style=”{StaticResource ChartStyle1}”>
          <chartingToolkit:Chart.Series>
               <me:GanttBarSeries x:Name=”BarSeries1″ DataPointStyle=”{StaticResource BarTemplateStyle1}”>
                    <chartingToolkit:BarSeries.DependentRangeAxis>
                         <chartingToolkit:LinearAxis Orientation=”X” AxisLabelStyle=”{StaticResource HorizontalAxisStyle}” />
                    </chartingToolkit:BarSeries.DependentRangeAxis>
                    <chartingToolkit:BarSeries.IndependentAxis>
                         <me:GanttYAxis x:Name=”customYAxis” Orientation=”Y” AxisLabelStyle=”{StaticResource VerticalAxisStyle}” />
                    </chartingToolkit:BarSeries.IndependentAxis>
               </me:GanttBarSeries>
          </chartingToolkit:Chart.Series>
     </me:GanttChart>
</Grid>

And finally I needed to set the Data Context for the GanttYAxis to the data points in the MainPage.xaml.cs codebehind where I’m setting everything up.

// Points show from bottom to top, so reverse it to show “1” at the top
dataPoints.Reverse();
((BarSeries)(mcChart.Series[0])).ItemsSource = dataPoints.ToArray();
((GanttYAxis)(((BarSeries)(mcChart.Series[0])).IndependentAxis)).DataContext = dataPoints.ToArray();

~Sue

Advertisements

Silverlight Gantt Part 5

I have been writing a series about how to create a simple Gantt chart using the latest version of the Silverlight Toolkit.  My links are in my first post, which you can get to from above.

To continue with the topics…

CHANGING THE SERIES BAR’S TOOL TIP

The only thing we really need to do is to find the following XAML:

<ToolTipService.ToolTip>
    
<ContentControl Content=”{TemplateBinding FormattedDependentValue}” />
</ToolTipService.ToolTip>

and change it to:

<ToolTipService.ToolTip>
    
<ContentControl>
         
<StackPanel Orientation=”Horizontal”>
              
<TextBlock Text=”{Binding StartDateDate, StringFormat=\{0:MM\/dd\/yyyy\}}”></TextBlock>
               
<TextBlock Text=” – “></TextBlock>
              
<TextBlock Text=”{Binding EndDateDate, StringFormat=\{0:MM\/dd\/yyyy\}}”></TextBlock>
         
</StackPanel>
    
</ContentControl>
</ToolTipService.ToolTip>

Now here, we’re using data binding from our custom class “DataItem” which I introduced in the last post.  We’re also telling it to format the resultant value in a particular manner, by using the StringFormat extension.  So as long as you’ve been following along, you should have this part completed.  Again, as I’ve said before, we’ll go over the full code for the preparation and binding in probably the last post in this series.

GETTING THE X AXIS TO DISPLAY DATES INSTEAD OF “Days from Min Date”

As I’ve said before, this implementation seems, on the surface, to be a bit messy.  What we’re doing again, is we’re saying, go get me the earliest date (let’s call it 8/1/2011 for sake of argument).  That Date now is going to be represented as the decimal “0” on the X Axis.  Whereas the date 8/25/2011 would be represented as the number “24” on the X Axis – the number of days FROM the minimum date.

Now again, this seems messy but I did try to use the DateTimeAxis object that is provided in the Toolkit.  I couldn’t get it to work using DateTimes.  I posted a forum post and was answered that only Doubles will work as the Dependent Value, not DateTimes.  So I offer that my solution while not pretty, is probably the practical way to do it.

Ok so the first thing to do is to create another Template for the X Axis.  But we also have to make sure we have an XMLNS in our Main Page:

xmlns:layout=”clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit”

So after we put the XMLNS in the  User Control, we add this new style to the UserControl.Resources section: 

<!– X Axis –>
<Style x:Key=”HorizontalAxisStyle” TargetType=”chartingToolkit:AxisLabel”>
     <Setter Property=”Margin” Value=”0,5,0,0″/>
     <Setter Property=”Template”>
          <Setter.Value>
               <ControlTemplate TargetType=”chartingToolkit:AxisLabel”>
                    <layout:LayoutTransformer>
                         <layout:LayoutTransformer.LayoutTransform>
                              <RotateTransform Angle=”-60″ />
                         </layout:LayoutTransformer.LayoutTransform>
                         <TextBlock 
                              Padding
=”0″ 
                              DataContext
=”{Binding}”
                             
Text=”{Binding Converter={StaticResource AxisDateFormatConverter}, ConverterParameter={StaticResource axisMinDate}}” />
                    </layout:LayoutTransformer>
               </ControlTemplate>
          </Setter.Value>
     </Setter>
</Style>

and of course set this new style in the chart markup

<chartingToolkit:BarSeries.DependentRangeAxis>
     <chartingToolkit:LinearAxis
          Orientation=”X”
         
AxisLabelStyle=”{StaticResource HorizontalAxisStyle}” />
    
</chartingToolkit:BarSeries.DependentRangeAxis>
<chartingToolkit:BarSeries.IndependentAxis>

The Control Template is doing 2 things here:  (1) It is rotating the labels of the X axis by a – 60 degrees; (2) it is printing the text, data bound to the X Axis Label, and using a Converter class with a Static Resource parameter.  Let’s first talk about the AxisDateFormatConverter class.

// Converter for showing the X Axis lables as DateTimes instead of doubles
// Expects the MinDate to be the smallest date of all of the data points
public class AxisDateFormatterIValueConverter
{
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
          if (parameter != null)
                {
               DateTime minDate = DateTime.MinValue;
               DateTime.TryParse(parameter.ToString(), out minDate); 

               if (minDate != DateTime.MinValue && minDate != DateTime.MaxValue)
                         {
                    DateTime dt = minDate.AddDays((double)value);
                    return dt.ToShortDateString();
                         }
                }
                return value;
        }

         public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    
{
                if (parameter != null)
          
{
                        DateTime minDate = DateTime.MinValue;
              
DateTime.TryParse(parameter.ToString(), out minDate);
             
DateTime endDate = DateTime.MaxValue;
             
DateTime.TryParse(value.ToString(), out endDate); 

                       if (minDate != DateTime.MinValue && endDate != DateTime.MaxValue)
             
{
                               return (endDate.Subtract(minDate)).TotalDays;
             
}
                }

                return value;
     
}
}

This class expects to receive the MinDate value, which is the Earliest StartDateDate of all of your data points.  It takes the number that was passed in, adds those in Days to the MinDate, and returns a string version of that Date.

One of the problems with this, is how to get the MinDate parameter into the function??  Well, I chose to use a Static Resource, made up of a light-weight custom class that simply holds the value for the Min Date:

// For use with a Static Resource, to pass in as a 
// parameter to the Conversion function for the
// X Axis values
public class AxisMinDate
{
        public DateTime Value { get; set; }

     public AxisMinDate()
    
{
        }

        public override string ToString()
    
{
                return Value.ToShortDateString();
    
}
}

Then in the UserControl.Resources section, I add the reference to this custom class, and add the reference to the AxisDateFormatter class as well:

<!– Axis Value Formatter –>
<me:AxisDateFormatter x:Key=”AxisDateFormatConverter” />
<me:AxisMinDate x:Key=”axisMinDate” />

In the code-behind, when setting up the min and max date, at the same time, you set it into your resource:

// Get the Min Date and Max Date for determining the intervals
DateTime maxDate = DateTime.MinValue;
DateTime minDate = DateTime.MaxValue;

foreach (DataItem p in dataPoints)
{
        if(p.EndDateDate > maxDate)
    
{
                maxDate = p.EndDateDate;
        }

        if(p.StartDateDate < minDate)
    
{
                minDate = p.StartDateDate;
        }
}

if (minDate > DateTime.Today)
{
        minDate = DateTime.Today.Subtract(newTimeSpan(10, 0, 0, 0));
}

// Set this date into a resource for use later in styling the X axis labels:
// Instead of numbers, we use dates, calculated as the MinDate plus the
// double Value of the dataPoint (in days).
((AxisMinDate)this.Resources[“axisMinDate”]).Value = minDate;

Next up:  Updating the X Axis Interval such that it only shows about 6-7 points at one time.

Silverlight Gantt Part 4

In my first post, I started with a teaser of what we’re trying to accomplish; in the second post, I talked about removing the Legend; and in the last post, I talked about putting a “Today” line on the chart.  In that last post, we covered some of the code necessary to data bind the chart, but really didn’t go into deep detail.

ADDING A TEXT LABEL INSIDE OR OUTSIDE OF THE BAR 

So if you look at the picture in the first post, you will notice that there is a Label at the end of the bar that shows the End Date.  That label is by default inside the bar, and has White text.  However, when the bar’s too small, it is displayed outside of the bar, using Black text.

We have to override the Style for the default BarDataPoint style.  Basically you’re telling it how to lay out the bars.  Now I’m going to post the entire style you need to put in your UserControl.Resources section.  Much of it is for stuff I don’t use, like highlighting and selecting; however I’m keeping it in there “just in case”.

So again, put this into your UserControl.Resources section:

<!– BarDataPoint Style and Template –>
<Style x:Key=”BarTemplateStyle1″ TargetType=”chartingToolkit:BarDataPoint”>
    
<Setter Property=”Template”>
         
<Setter.Value>
              
<ControlTemplate x:Name=”BarTemplate” TargetType=”chartingToolkit:BarDataPoint”>
                    
<Grid x:Name=”BarTemplateMainGrid”>
                        
<VisualStateManager.VisualStateGroups>
                             
<VisualStateGroup x:Name=”CommonStates”>
                                  
<VisualStateGroup.Transitions>
                                       
<VisualTransition GeneratedDuration=”0:0:0.1″/>
                                  
</VisualStateGroup.Transitions>
                                  
<VisualState x:Name=”Normal”/>
                                  
<VisualState x:Name=”MouseOver”>
                                       
<Storyboard>
                                            
<DoubleAnimation
                                                  Duration
=”0″ 
                                                  Storyboard.TargetName
=”MouseOverHighlight”
                                                  Storyboard.TargetProperty
=”Opacity”
                                                  To
=”0.6″/>
                                       
</Storyboard>
                                  
</VisualState>
                             
</VisualStateGroup>
                             
<VisualStateGroup x:Name=”SelectionStates”>
                                   <VisualStateGroup.Transitions>
                                       
<VisualTransition GeneratedDuration=”0:0:0.1″/>
                                   </VisualStateGroup.Transitions>
                                  
<VisualState x:Name=”Unselected”/>
                                  
<VisualState x:Name=”Selected”>
                                       
<Storyboard>
                                             <DoubleAnimation
                                                  Duration
=”0″
                                                  Storyboard.TargetName
=”SelectionHighlight”
                                                  Storyboard.TargetProperty
=”Opacity”
                                                  To
 =”0.6″/>
                                       
</Storyboard>
                                  
</VisualState>
                             
</VisualStateGroup>
                              
<VisualStateGroup x:Name=”RevealStates”>
                                  
<VisualStateGroup.Transitions>
                                       
<VisualTransition GeneratedDuration=”0:0:0.5″/>
                                  
</VisualStateGroup.Transitions>
                                  
<VisualState x:Name=”Shown”>
                                       
<Storyboard/>
                                  
</VisualState>
                                  
<VisualState x:Name=”Hidden”>
                                       
<Storyboard/>
                                  
</VisualState>
                             
</VisualStateGroup>
                        
</VisualStateManager.VisualStateGroups>
                        
<ToolTipService.ToolTip>
                              <!– ######### THIS WILL BE CHANGED LATER –>
                              
<ContentControl Content=”{TemplateBinding FormattedDependentValue}”/>
                         </ToolTipService.ToolTip>
                        
<Rectangle x:Name=”SelectionHighlight”
                              Opacity
=”0″ 
                              Fill
=”Red” 
                              Grid.RowSpan
=”2″
                              Grid.ColumnSpan
=”3″
                              Margin
=”1.332,0,-1.332,0″/>
                        
<Rectangle x:Name=”MouseOverHighlight”
                              Opacity
=”0″
                              Fill
=”White”
                              Grid.RowSpan
=”2″ 
                              Grid.ColumnSpan
=”3″/>
                        
<Grid x:Name=”GanttDataPointBody”>
                             
<Rectangle Fill=”#FF284B70″/>
                             
<Path Stretch=”Fill” 
                                   Data
=”F1 M16.9979,-38.6618 L304.96201,-38.6618 L295.85101,-12.98486 C293.37601,-10.55966 290.95508,-6.5848103 288.04709,-5.3772006 C285.1391,-4.1696005 266.16092,-4.4174142 262.82092,-4.4273643 L60.699768,-5.1715899 C56.235268,-5.1848698 37.786591,-5.2874784 34.107491,-6.7409983 C30.428286,-8.1945286 28.571583,-13.514527 25.677784,-16.408327 L16.9979,-38.6618 z”
                                   Height
=”10.008″
                                   VerticalAlignment
=”Top”
                                   d
:LayoutOverrides=”Height, GridBox”>
                                  
<Path.Fill>
                                       
<LinearGradientBrush StartPoint=”0.506944,-0.479586″ EndPoint=”0.506944,0.980026″>
                                            
<GradientStop Color=”#00000000″ Offset=”0″/>
                                             
<GradientStop Color=”#FFB9D6F7″ Offset=”0.496″/>
                                            
<GradientStop Color=”#FF284B70″ Offset=”1″/>
                                       
</LinearGradientBrush>
                                  
</Path.Fill>
                              
</Path>
                             
<!– ######### ADDITION HERE –>
                             
<TextBlock
                                  
x:Name=”DataPointLabel”
                                  
Foreground=”{Binding Foreground}”
                                  
Margin=”{Binding TextLabelMargin}”
                                  
VerticalAlignment=”Center”
                                  
TextAlignment=”Right”
                                   
Text=”{Binding FormattedValue}”>
                              </
TextBlock>
                             
<!– ######### END ADDITION –>
                         
</Grid>
                    
</Grid>
               
</ControlTemplate>
          
</Setter.Value>
     
</Setter>
</Style>

And now you need to update your BarSeries element inside your Chart to point to the new style.

<chartingToolkit:BarSeries
    
x:Name=”BarSeries1″
    
DataPointStyle=”{StaticResource BarTemplateStyle1}”>

 You will notice that in the textblock we have some binding going on

 <!– ######### ADDITION HERE –>
<TextBlock
    
x:Name=”DataPointLabel”
    
Foreground=”{Binding Foreground}”
    
Margin=”{Binding TextLabelMargin}”
    
VerticalAlignment=”Center”
    
TextAlignment=”Right”
    
Text=”{Binding FormattedValue}”>
</
TextBlock>
<!– ######### END ADDITION –>

Well what we’ve done here, is that the Bar Series will be Data Bound to a Custom Class that we create to hold the data.  Let’s take a look at that custom class:

public class DataItem
{
         publicstring Key { get; set; }
    
public DateTime MinDate { get; set; }
    
public double StartDate { get; set; }
    
public double EndDate { get; set; }
    
public string TextLabelMargin { get; set; }
    
public string Foreground { get; set; }

     public
DateTime StartDateDate { get; set; }
    
public DateTime EndDateDate { get; set; }

     public string FormattedValue
    
{
                   get
          
{
                          returnstring.Format(“{0:MM/dd/yyyy}”, EndDateDate);
           
}
        }

        public void SetDateDoubles()
    
{
                if (MinDate != DateTime.MinValue)
         
{
                         StartDate = (StartDateDate.Subtract(MinDate)).Days;
                         EndDate = (EndDateDate.Subtract(MinDate)).Days;
                 }
         }

         public DataI tem(string _key, DateTime _start, DateTime _end, string _margin, string _foreground)
    
{
               Key = _key;
               StartDateDate = _start;
               EndDateDate = _end;
               Foreground = _foreground;
        }

        public DataItem()
    
{
         }

We basically populate it with the Start and End dates, and provide it a “Minimum Date” to calculate the number of days it’s been since the first date on the graph we want to use.  So for example if the MinDate was 10/1/2011 and the StartDateDate was 10/11/2011 then the StartDate value would be 10.  That’s how we’re using Doubles to plot the values on the X axis.

When we go through setting up our values into this custom class, we use the default values of “0,0,10,0” for the Margin, which puts a 10 pixel margin on the right (the text box is right-aligned).  We start with the default Foreground color of “White”.

foreach (ListItem item in spItems)
{
         try
    
{
                 // Populate the DataItem collection (dataPoints)
          
//…

          DataItem point = new DataItem(key, StartDate, EndDate, “0,0,10,0”, “White”);

                dataPoints.Add(point);
        }
        catch (Exception ex1)
    
{
                MessageBox.Show(ex1.ToString());
    
}
}

Then we calculate the Minimum and Maximum dates, so that we can set those values in the data points.  Yea, it’s a twice go-round of the data points, but it was either that or make 2 Server calls.

// Get the Min Date and Max Date for determining the intervals
DateTime maxDate = DateTime.MinValue;
DateTime minDate = DateTime.MaxValue;

foreach (DataItem p in dataPoints)
{
        if(p.EndDateDate > maxDate)
    
{
                maxDate = p.EndDateDate;
        }

        if(p.StartDateDate < minDate)
    
{
               minDate = p.StartDateDate;
        }
}

if (minDate > DateTime.Today)
{
        minDate =DateTime.Today.Subtract(newTimeSpan(10, 0, 0, 0));
}

// Total number of days from start to finish
double max = (maxDate.Subtract(minDate)).Days;

if(max == 0)
{
        mcChart.Title =“Sample Data”;
    
return;
}

foreach (DataItem p in dataPoints)
{
        p.MinDate = minDate;
        p.SetDateDoubles(); 

     // This is an approximation, as the actual X Axis points 
    
// have not yet been set
     
int daysWidth = (p.EndDateDate.Subtract(p.StartDateDate)).Days;

     if(daysWidth < 100)
    
{
                p.TextLabelMargin =“-75,0,-75,0”;
         
p.Foreground =“Black”;
    
}
}

By setting a negative Right margin here, you effectively bring it outside and to the right of the end of the bar.  I noticed I also had to change the left margin too, or the date was getting cut off for some reason.

Next you data bind, but again, we’ll be talking more in depth about that in a later post, probably toward the end of the series.

Next time, Changing the ToolTip for the bar.  See you next time!

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.