Displaying a Microsoft Project XML file using Diagrammer for .NET, WPF and LinQ (Part 1)

Diagrammer for .NET 1.5 introduces a set of new WPF controls that can present information as a graph display made of nodes and links. In this post, I will show how you can use these controls to display the content of a Microsoft Project XML file. We will use .NET 3.5 and Linq to read the data and provide it to the ILOG WPF Diagram control.

The XML that you can export from Microsoft Project 2003 contains all the information on the project plan such as all the tasks, resources, calendars and more. Here we will only take care of the tasks and the predecessor constraints between tasks. Predecessor constraints are the constraints between tasks such as “task A starts after task B”. The graph that we will create will have nodes representing the tasks and links representing the predecessor constraints between tasks. The result will give something similar to the “Network Diagram” view of Microsoft Project that represents nicely the flow of tasks in the project plan.

First of all, a few words on the Microsoft Project XML format. The format defines a <tasks> tag that contains all the tasks, each task is represented by a <task> tag that contains the task information.

<Project xmlns="http://schemas.microsoft.com/project">
...
<Tasks>
  <Task>
    <UID>Unique id of task</UID>
    <Name>The name</Name>
    <Start>The start date </Start>
    <Finish>The finish date</Finish>
     ...
    </Task>
</Tasks>
...
</Project>

When a task has predecessors, a <PredecessorLink> tag appears for each predecessor in the <task> tag:

<Task>
  ...
  <PredecessorLink>
    <PredecessorUID>1</PredecessorUID>
    <Type>1</Type>
  </PredecessorLink>
  ...
</Task>

<PredecessorUID> corresponds to the unique id (UID tag) of the predecessor and <Type> gives the type of the constraint (0 for a “Finish to Finish” constraint, 1 for “Finish to Start”, 2 for “Start to Finish” and 3 for “Start to Start”).

That’s all we need to know to be able to read the data.

We now define very simple .NET classes that correspond to this structure, a class Project that represents the project, a Task and a PredecessorLink class:

public class Project
{
    public List<Task> Tasks { get; set; }
} 

public class PredecessorLink
{
    public int PredecessorUID { get; set; }
} 

public class Task
{
    public Project Project { get; set; }
    public int UID { get; set; }
    public string Name { get; set; }
    public DateTime Start { get; set; }
    public DateTime Finish { get; set; }
    public List<PredecessorLink> PredecessorLinks { get; set; }
}

Thanks to Linq for XML, we can now read the data from the XML file and directly create the Project, Task and PredecessorLink instances.

XDocument document = XDocument.Load("./Engineering.xml");
Project project = new Project();
project.Tasks
      = (from task in document.Descendants("{http://schemas.microsoft.com/project}Task")
          where (int)task.Element("{http://schemas.microsoft.com/project}ID") != 0
          select new Task()
           {
               Project = project,
               UID = (int)task.Element("{http://schemas.microsoft.com/project}UID"),
               Name = (string)task.Element("{http://schemas.microsoft.com/project}Name"),
               Start = (DateTime)task.Element("{http://schemas.microsoft.com/project}Start"),
               Finish = (DateTime)task.Element("{http://schemas.microsoft.com/project}Finish"),
               PredecessorLinks =
                         (from link in task.Descendants("{http://schemas.microsoft.com/project}PredecessorLink")
                         select
                           new PredecessorLink
                           {
                               PredecessorUID = (int)link.Element("{http://schemas.microsoft.com/project}PredecessorUID")
                           }).ToList() 

              }).ToList();

The LinQ code above will iterate over the <Task> elements in the XML document and create a Task instance, then for each Task he will create the list of PredecessorLink. Note that since the XML document defines a default namespace (http://schemas.microsoft.com/project), we need to specify this namespace when querying the document with Linq and this makes the code not really easy to read.

So far we have only been playing with data, we will now start to use WPF to render this information. We have a Project instance that contains a list of Task, each task containing information on its predecessors. That is almost all the information that is required to use the Diagrammer for .NET WPF Diagram control.

Let’s start the XAML code by using the Diagram control:

<Window x:Class="CriticalPath.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ilog="http://www.ilog.com/diagrammer.net/2008"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <ilog:Diagram x:Name=”diagram”/>
   </Grid>
</Window>

We have named our control “diagram”, we can now specify the data source in the code behind:

  diagram.Source = project.Tasks;

The diagram control now knows about the data source, but this information is not enough to create a graph display. At this point the result of the application is the following:
first.png
Not so impressive…
The Diagram control does not know the relation between the tasks and thus it has layout the task as a simple vertical list with no links between them. We have not defined any graphical representation for the nodes that’s why we get a simple rectangle with the name of the class (in this case this is the result of calling ToString() on the Task instances). Let’s make this look nicer.
First we need to tell the Diagram that there are some relations between them. For this we use the PredecessorBinding property of the Diagram control.
This PredecessorBinding property tells the control how from a Task (an item in the data source) he can find a list of predecessor Tasks (predecessor items in the data source). With this information, the Diagram control can create links between the nodes and layout the graph. So far the Task class does not have a property that returns the predecessor tasks, we only have the predecessor links. Let’s use LinQ again to create a property that returns the list of predecessor tasks:

public class Task
{
    public Project Project { get; set; }
    public int UID { get; set; }
    public string Name { get; set; }
    public DateTime Start { get; set; }
    public DateTime Finish { get; set; }
    public List<PredecessorLink> PredecessorLinks { get; set; }
    public List Predecessors
    {
      get
      {
        return
          (from pred in PredecessorLinks
           join task in Project.Tasks on pred.PredecessorUID equals task.UID
           select task).ToList();
      }
    }
    public override string ToString()
    {
       return Name;
    }
}

The new Predecessors property now returns the list of predecessor tasks by doing a LinQ join between the PredecessorUID in the PredecessorLink and the UID of tasks.

We can now change the XAML and use this new method:

    <ilog:Diagram
        x:Name="diagram"
        PredecessorsBinding=”{Binding Predecessors}”>

Now the control knows about the relation between tasks. Note that we have also overridden the ToString method of the Task so that it returns the name of the task. Now we get the following graph that displays the constraints between tasks nicely layout:

Now that the data is present, we can exercise our graphic designer skills using the styling and templating features of WPF to make this graph look better.
The Diagram control has created instances of the Node and Link classes for nodes and links. We can then define a WPF style for these two classes:

<Style TargetType="{x:Type ilog:Link}" >
  <Setter Property="Radius" Value="10"/>
  <Setter Property="Stroke" Value="#FFF0A5"/>
  <Setter Property="StrokeThickness" Value="2"/>
  <Setter Property="StartArrow">
    <Setter.Value>
      <ilog:LinkArrow  Fill="White"  Shape="Circle"/>
    </Setter.Value>
  </Setter>
  <Setter Property="EndArrow">
    <Setter.Value>
      <ilog:LinkArrow Fill="{x:Null}"  Shape="Open"/>
    </Setter.Value>
  </Setter>
</Style> 

<Style TargetType="{x:Type ilog:Node}">
  <Setter Property="Background" Value="#FFF0A5"/>
  <Setter Property="BorderBrush" Value="#468966"/>
  <Setter Property="BorderThickness" Value="1"/>
</Style>

The style for link defines the color and thickness of links through the Stroke and StrokeThickness properties. It also defines a start and end arrow with different shapes. The style for nodes defines the colors for the background and border of the node.
We are now going to display more information in the node that the simple name of the task. This is done through a WPF DataTemplate. The DataTemplate that we will specify through the NodeTemplate property of the Diagram control represents the graphical template that will be instantiated for each node. Here we will display not only the name but also the start and finish date of the task.

<DataTemplate x:Key="TaskTemplate">
  <Grid  Margin="5" MaxWidth="150" x:Name="grid">
    <Grid.RowDefinitions>
      <RowDefinition/>
      <RowDefinition/>
      <RowDefinition/>
    </Grid.RowDefinitions>
    <TextBlock
       x:Name="header" Grid.Row ="0"
       Text="{Binding Path=Name}"
       TextWrapping="WrapWithOverflow"
       Foreground="#FFB03B"
       FontWeight="Bold"/>
    <StackPanel x:Name="startField"  Grid.Row ="1" Orientation="Horizontal">
      <TextBlock x:Name="startLabel" Text="Start:"/>
      <TextBlock Text="{Binding Path=Start}"/>
    </StackPanel>
    <StackPanel x:Name="finishField" Grid.Row ="2" Orientation="Horizontal">
      <TextBlock x:Name="finishLabel" Text="Finish:"/>
      <TextBlock Text="{Binding Path=Finish}"/>
    </StackPanel>
  </Grid>
</DataTemplate>

This data template defines a grid with 3 rows, first row will display the name of the task, the second row the start date and the last row the finish date.

The Diagram control layouts the graph using a graph layout algorithm. ILOG Diagrammer for .NET provides a large set of graph layout algorithms that can be use to represents graph, in a Diagram the default algorithm is a Hierarchical layout algorithm. This algorithm suits perfectly for our needs because it shows nicely the flow of information in the graph. We will only modify some of the layout properties to get a nicer result. Let’s use orthogonal links and links evenly spaced on the side of nodes. We do this by specifying the layout algorithm through the GraphLayout property of the Diagram class in XAML.
After adding a gradient for the background of the diagram through the Background property, the XAML is now:

<ilog:Diagram
  NodeTemplate="{StaticResource TaskTemplate}"
  PredecessorsBinding="{Binding Predecessors}" x:Name="diagram"> 

  <!-- Background of diagram -->
  <ilog:Diagram.Background>
    <LinearGradientBrush  EndPoint="0.5,1" StartPoint="0.5,0">
      <GradientStop Color="#FF555555" Offset="1"/>
      <GradientStop Color="#FF1C1C1C" Offset="0"/>
    </LinearGradientBrush>
  </ilog:Diagram.Background> 

  <!-- graph layout algorithm -->
  <ilog:Diagram.GraphLayout>
    <ilog:HierarchicalLayout
          LinkStyle="Orthogonal"
          ConnectorStyle="EvenlySpaced" />
  </ilog:Diagram.GraphLayout>
</ilog:Diagram>

And the resulting graph is now much better.

We have seen how easy it is to create a graph display using LinQ, WPF styling and templating and the Diagram control. In a second post I will show how you can go further and tailor the graph with WPF features such as data triggers and more features of the Diagram class. In particular, we will see how to display the critical path of the project plan.

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Netvouz
  • DZone
  • ThisNext
  • MisterWong
  • Wists

One Response to “Displaying a Microsoft Project XML file using Diagrammer for .NET, WPF and LinQ (Part 1)”

  1. WPF/Silverlight/XAML Web News - 2008/05/23 - Rob Relyea - Xamlified Says:

    […] ILOG Visualization .NET Blog: Displaying a MS Project XML file using Diagrammer for .NET, WPF, and LinQ Part1 […]

Leave a Reply