Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

Category Archives: Client Object Model

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.

SharePoint 2010 Remote Metadata – Client Object Model and WS

Today I was playing with an Add-In to have an attachment go from Microsoft Outlook straight to SharePoint into a Document Library, but they have to specify the Metadata, and one of the metadata fields is of type “Managed Metadata”.  So I had to figure out how to remotely get the choices for the metadata fields – i.e. the terms in the term set that apply only to that column.

So it turns out that you can get the SchemaXml property off of a field in a List and that will return a lot of information to you, like what some of the key ID’s are.  Below is an example where I’m getting key information to be able to dynamically display a control for the metadata field in question:

ClientContext spCtx = new ClientContext(webURL);
Web remoteWeb = spCtx.Web;

List docLib = remoteWeb.Lists.GetByTitle(listName);
spCtx.Load(docLib);
spCtx.Load(docLib.Fields);
spCtx.ExecuteQuery();
Control lastControl = null;
foreach (Field field in docLib.Fields)
{
 int top = 5;
 if (lastControl != null)
 {
  top = lastControl.Top + lastControl.Height + 5;
 }
 if (!field.Hidden && !field.ReadOnlyField)
 {
  if (field.InternalName != "FileLeafRef" &&
   field.InternalName != "ContentType" &&
   field.Title != "Title")
  {
   // Title will be same as File Leaf Ref (Name)
   Label newLbl = new Label();
   newLbl.AutoSize = false;
   newLbl.Width = 200;
   newLbl.Top = top;
   newLbl.Left = 5;
   newLbl.Text = field.Title;
   pnlControls.Controls.Add(newLbl);
   switch (field.TypeDisplayName)
   {
    case "Managed Metadata":
     string xml = field.SchemaXml;
     XmlDocument xDoc = new XmlDocument();
     xDoc.LoadXml(xml);
     string termSetID = string.Empty;
     string anchorID = string.Empty;
     string sspID = string.Empty;
     XmlNode termSetIDNode = xDoc.SelectSingleNode("//Property/Value[../Name = 'TermSetId']");
     if (termSetIDNode != null)
     {
      termSetID = termSetIDNode.InnerText;
     }
     XmlNode anchorIDNode = xDoc.SelectSingleNode("//Property/Value[../Name = 'AnchorId']");
     if (anchorIDNode != null)
     {
      anchorID = anchorIDNode.InnerText;
     }
     XmlNode sspIDNode = xDoc.SelectSingleNode("//Property/Value[../Name = 'SspId']");
     if (sspIDNode != null)
     {
      sspID = sspIDNode.InnerText;
     }
     ManagedMetadataField mmdFld = new ManagedMetadataField(webURL, sspID, termSetID, anchorID);
     mmdFld.Left = 210;
     mmdFld.Top = top;
     mmdFld.Tag = field.InternalName;
     lastControl = mmdFld;
     pnlControls.Controls.Add(mmdFld);
     break;
    default:
     TextBox slineOText = new TextBox();
     slineOText.Width = 264;
     slineOText.Tag = field.InternalName;
     slineOText.Left = 210;
     slineOText.Top = top;
     lastControl = slineOText;
     pnlControls.Controls.Add(slineOText);
     break;
   }
  }
 }
}

Then, in the user control for the Managed Metadata Field type, I call on the TaxonomyClientService web service to get the terms in the term set.  The code is below, however I don’t know what the significance of the <timestamp> piece is yet.  Also, I don’t know if those @a9’s and @a45’s and such actually change names depending on ??

List<Term> relevantTerms = new List<Term>();
if (!string.IsNullOrEmpty(termSetID))
{
 taxonomyWS.Taxonomywebservice ws = new taxonomyWS.Taxonomywebservice();
 ws.Url = webURL + "/_vti_bin/TaxonomyClientService.asmx";
 // TODO
 ws.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
 string oldtimestamp = "<timeStamp>633992461437070000</timeStamp>";
 string clientVersion = "<version>3</version>";
 string termStoreIds = "<termStoreId>" + sspID + "</termStoreId>";
 string termSetIds = "<termSetId>" + termSetID + "</termSetId>";
 int lcidEnglish = System.Globalization.CultureInfo.GetCultureInfo("en-US").LCID;
 string timeStamp = string.Empty;
 string termSet = ws.GetTermSets(termStoreIds, termSetIds, lcidEnglish, oldtimestamp, clientVersion, out timeStamp);
 XmlDocument xDoc = new XmlDocument();
 xDoc.LoadXml(termSet);
 XmlNodeList relevantTermNodes = null;
 if (!string.IsNullOrEmpty(anchorID) && anchorID != "00000000-0000-0000-0000-000000000000")
 {
  XmlNode anchorNode = xDoc.SelectSingleNode("//T[@a9 = '" + anchorID + "']");
  if (anchorNode != null)
  {
   relevantTermNodes = xDoc.SelectNodes("//T[contains(TMS/TM/@a45,'" + anchorID + "')]");
  }
 }
 
 if(relevantTermNodes == null)
 {
  relevantTermNodes = xDoc.SelectNodes("//T");
 }
 foreach (XmlNode node in relevantTermNodes)
 {
  string id = string.Empty;
  string name = string.Empty;
  string path = string.Empty;
  XmlNode idNode = node.SelectSingleNode("@a9");
  if (idNode != null)
  {
   id = idNode.InnerText;
  }
  XmlNode pathNode = node.SelectSingleNode("TMS/TM/@a40");
  if (pathNode != null)
  {
   path = pathNode.InnerText;
  }
  XmlNodeList nameNodes = node.SelectNodes("LS/TL/@a32");
  bool first = true;
  List<string> extraNames = new List<string>();
  foreach (XmlNode nameNode in nameNodes)
  {
   if (nameNode != null)
   {
    if (first)
    {
     name = nameNode.InnerText;
    }
    else
    {
     extraNames.Add(nameNode.InnerText);
    }
   }
   first = false;
  }
  if (!string.IsNullOrEmpty(id) &&
   !string.IsNullOrEmpty(name))
  {
   Term newTerm = new Term(id, name, path);
   if (extraNames.Count > 0)
   {
    newTerm.AlternateTerms = extraNames;
   }
   relevantTerms.Add(newTerm);
  }
 }
}
relevantTerms.Sort();
return relevantTerms;

Note the use here of a custom class called Term and the fact that you can have Alternate Terms.

Finally, when I go to return the final selected value, you have to get the WssID from the hidden list called TaxonomyHiddenList.

public string GetSelectedValue()
{
 if(!string.IsNullOrEmpty(txtTermValue.Text))
 {
  foreach (Term term in terms)
  {
   if (term.TermName.ToLower() == txtTermValue.Text.ToLower())
   {
    // Found the right term
    return GetWssIDByTermID(term.TermID) + ";#" + term.TermName + "|" + term.TermID;
   }
  }
 }
 return null;
}
private string GetWssIDByTermID(string termID)
{
 int result = -1;
 try
 {
  ClientContext spCtx = new ClientContext(webURL);
  Web web = spCtx.Web;
  List hiddenList = web.Lists.GetByTitle("TaxonomyHiddenList");
  CamlQuery query = new CamlQuery();
  query.ViewXml = "<View><Query><Where><Eq><FieldRef Name='IdForTerm' /><Value Type='Text'>" + termID + "</Value></Eq></Where></Query></View>";
  ListItemCollection items = hiddenList.GetItems(query);
  spCtx.ExecuteQuery();
  if (items != null)
  {
   spCtx.Load(items);
   spCtx.ExecuteQuery();
   if (items.Count > 0)
   {
    result = Convert.ToInt32(items[0]["ID"]);
   }
  }
 }
 catch
 {
  result = -1;
 }
 return result.ToString();
}