Pretty Cool Graphs In WPF

I have just finished writing up a new article for www.codeproject.com for which I will write another blog post about. Thing is, when I was looking into parts of that article I wanted to use some graphs in WPF, and had to have a hunt around to see what was out there.

After a look about I found a truly excellent graphing library for WPF which is completely free. It’s a www.codeplex.com project called “GraphSharp” which is freely available at graphsharp.codeplex.com.

I also figured it may be good to show people how to get up and running with GraphSharp in this standalone blog as the new article I published does absolutely no hand holding about the graph creation, as that was not really what the new article is about.

So how do we use GraphSharp then, well we need to do a couple of things

1. Create a customised Vertex type

The 1st step is to define a custom Vertex for the graph such that you can store additional information for each Vertex. This is done as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace GraphSharpDemo
{
    /// 
    /// A simple identifiable vertex.
    /// 
    [DebuggerDisplay("{ID}-{IsMale}")]
    public class PocVertex
    {
        public string ID { get; private set; }
        public bool IsMale { get; private set; }

        public PocVertex(string id, bool isMale)
        {
            ID = id;
            IsMale = isMale;
        }

        public override string ToString()
        {
            return string.Format("{0}-{1}", ID, IsMale);
        }
    }
}

2. Create a customised Edge type

The next step is to define a custom Edge type, you may want to show some extra information when the Edge is hovered over for example. This is done as follows:

using QuickGraph;
using System.Diagnostics;

namespace GraphSharpDemo
{
    /// 
    /// A simple identifiable edge.
    /// 
    [DebuggerDisplay("{Source.ID} -> {Target.ID}")]
    public class PocEdge : Edge
    {
        public string ID
        {
            get;
            private set;
        }

        public PocEdge(string id, PocVertex source, PocVertex target)
            : base(source, target)
        {
            ID = id;
        }
    }
}

3. Create a customised Graph

If you have custom Vertex/Edge you will need to create a custom Graph that knows about these custom types. Again this is very easily achieved, using the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QuickGraph;

namespace GraphSharpDemo
{
    public class PocGraph : BidirectionalGraph
    {
        public PocGraph() { }

        public PocGraph(bool allowParallelEdges)
            : base(allowParallelEdges) { }

        public PocGraph(bool allowParallelEdges, int vertexCapacity)
            : base(allowParallelEdges, vertexCapacity) { }
    }
}

4. Create a customised GraphLayout

Using GraphSharp we also need to create a custom LayoutTyppe for the custom graph, which is done as follows:

public class PocGraphLayout : GraphLayout { }

5. Create a ViewModel

Since we are using WPF, why not follow best practices and use MVVM for it, which obviously means creating a ViewModel. Here is an example ViewModel that can be set as the DataContext for a Window/UserControl that hosts the GraphSharp graphing controls.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using GraphSharp.Controls;

namespace GraphSharpDemo
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        #region Data
        private string layoutAlgorithmType;
        private PocGraph graph;
        private List layoutAlgorithmTypes = new List<string>();
        #endregion

        #region Ctor
        public MainWindowViewModel()
        {
            Graph = new PocGraph(true);

            List existingVertices = new List();
            existingVertices.Add(new PocVertex("Sacha Barber", true)); //0
            existingVertices.Add(new PocVertex("Sarah Barber", false)); //1
            existingVertices.Add(new PocVertex("Marlon Grech", true)); //2
            existingVertices.Add(new PocVertex("Daniel Vaughan", true)); //3
            existingVertices.Add(new PocVertex("Bea Costa", false)); //4

            foreach (PocVertex vertex in existingVertices)
                Graph.AddVertex(vertex);

            //add some edges to the graph
            AddNewGraphEdge(existingVertices[0], existingVertices[1]);
            AddNewGraphEdge(existingVertices[0], existingVertices[2]);
            AddNewGraphEdge(existingVertices[0], existingVertices[3]);
            AddNewGraphEdge(existingVertices[0], existingVertices[4]);

            AddNewGraphEdge(existingVertices[1], existingVertices[0]);
            AddNewGraphEdge(existingVertices[1], existingVertices[2]);
            AddNewGraphEdge(existingVertices[1], existingVertices[3]);

            AddNewGraphEdge(existingVertices[2], existingVertices[0]);
            AddNewGraphEdge(existingVertices[2], existingVertices[1]);
            AddNewGraphEdge(existingVertices[2], existingVertices[3]);
            AddNewGraphEdge(existingVertices[2], existingVertices[4]);

            AddNewGraphEdge(existingVertices[3], existingVertices[0]);
            AddNewGraphEdge(existingVertices[3], existingVertices[1]);
            AddNewGraphEdge(existingVertices[3], existingVertices[3]);
            AddNewGraphEdge(existingVertices[3], existingVertices[4]);

            AddNewGraphEdge(existingVertices[4], existingVertices[0]);
            AddNewGraphEdge(existingVertices[4], existingVertices[2]);
            AddNewGraphEdge(existingVertices[4], existingVertices[3]);

            string edgeString = string.Format("{0}-{1} Connected", 
                existingVertices[0].ID, existingVertices[0].ID);
            Graph.AddEdge(new PocEdge(edgeString, existingVertices[0], existingVertices[1]));
            Graph.AddEdge(new PocEdge(edgeString, existingVertices[0], existingVertices[1]));
            Graph.AddEdge(new PocEdge(edgeString, existingVertices[0], existingVertices[1]));
            Graph.AddEdge(new PocEdge(edgeString, existingVertices[0], existingVertices[1]));

            //Add Layout Algorithm Types
            layoutAlgorithmTypes.Add("BoundedFR");
            layoutAlgorithmTypes.Add("Circular");
            layoutAlgorithmTypes.Add("CompoundFDP");
            layoutAlgorithmTypes.Add("EfficientSugiyama");
            layoutAlgorithmTypes.Add("FR");
            layoutAlgorithmTypes.Add("ISOM");
            layoutAlgorithmTypes.Add("KK");
            layoutAlgorithmTypes.Add("LinLog");
            layoutAlgorithmTypes.Add("Tree");

            //Pick a default Layout Algorithm Type
            LayoutAlgorithmType = "LinLog";

        }
        #endregion

        #region Private Methods
        private PocEdge AddNewGraphEdge(PocVertex from, PocVertex to)
        {
            string edgeString = string.Format("{0}-{1} Connected", from.ID, to.ID);

            PocEdge newEdge = new PocEdge(edgeString, from, to);
            Graph.AddEdge(newEdge);
            return newEdge;
        }

        #endregion

        #region Public Properties

        public List LayoutAlgorithmTypes
        {
            get { return layoutAlgorithmTypes; }
        }

        public string LayoutAlgorithmType
        {
            get { return layoutAlgorithmType; }
            set
            {
                layoutAlgorithmType = value;
                NotifyPropertyChanged("LayoutAlgorithmType");
            }
        }

        public PocGraph Graph
        {
            get { return graph; }
            set
            {
                graph = value;
                NotifyPropertyChanged("Graph");
            }
        }
        #endregion

        #region INotifyPropertyChanged Implementation

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #endregion
    }
}

6. Create and host the Graph Controls

So work with the GraphSharp Graph you need to use the GraphSharp UserControls for WPF. Here is what you need to do where I am using the MainWindowViewModel as just shown to bind against:

<Window x:Class="GraphSharpDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:graphsharp="clr-namespace:GraphSharp.Controls;assembly=GraphSharp.Controls"
        xmlns:local="clr-namespace:GraphSharpDemo"
        xmlns:zoom="clr-namespace:WPFExtensions.Controls;assembly=WPFExtensions"        
        Title="GraphSharpDemo" Height="350" Width="525">

        <zoom:ZoomControl  Grid.Row="1"  Zoom="0.2" 
        ZoomBoxOpacity="0.5" Background="#ff656565">
            <local:PocGraphLayout x:Name="graphLayout" Margin="10"
        Graph="{Binding Path=Graph}"
        LayoutAlgorithmType="{Binding Path=LayoutAlgorithmType, Mode=OneWay}"
        OverlapRemovalAlgorithmType="FSA"
        HighlightAlgorithmType="Simple" />

        </zoom:ZoomControl>
</Window>

7. Create Templates For Graph Vertices/Edges

Finally all that is left to do, is create some DataTemplates that will show what you want for your custom Graph Vertices/Edges. Here are some examples for the custom Vertices/Edges above

<DataTemplate x:Key="demoTemplate" DataType="{x:Type local:PocVertex}">
    <StackPanel Orientation="Horizontal" Margin="5">
        <Image x:Name="img" Source="../Images/boy.ico" Width="20" Height="20" />
        <TextBlock Text="{Binding Path=ID, Mode=OneWay}" Foreground="White" />
    </StackPanel>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding IsMale}" Value="false">
            <Setter TargetName="img" Property="Source"
                        Value="../Images/girl.ico" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

<Style TargetType="{x:Type graphsharp:VertexControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type graphsharp:VertexControl}">
                <Border BorderBrush="White" 
                        Background="Black"
            BorderThickness="2"
            CornerRadius="10,10,10,10"
            Padding="{TemplateBinding Padding}">
                    <ContentPresenter Content="{TemplateBinding Vertex}" 
                            ContentTemplate="{StaticResource demoTemplate}"/>

                    <Border.Effect>
                        <DropShadowEffect BlurRadius="2" Color="LightGray" 
                            Opacity="0.3" Direction="315"/>
                    </Border.Effect>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="{x:Type graphsharp:EdgeControl}">
    <Style.Resources>
        <ToolTip x:Key="ToolTipContent">
            <StackPanel>
                <TextBlock FontWeight="Bold" Text="Edge.ID"/>
                <TextBlock Text="{Binding ID}"/>
            </StackPanel>
        </ToolTip>
    </Style.Resources>
    <Setter Property="ToolTip" Value="{StaticResource ToolTipContent}"/>
</Style>

Putting it all together it looks like this:

image

As always here is a small demo app : GraphSharpDemo.zip

Advertisements

66 thoughts on “Pretty Cool Graphs In WPF

  1. Stone says:

    Hello Sacha,

    as always a vera, very good post. Thank you!

    (Maybe yhou have to fix some links in your article. The links to Graph# are pre-tagged with “sachabarber.net”.)

    Cheers!

    Stone

  2. sacha says:

    I will fix that

  3. Martin BG says:

    Dude, thank you! I have been looking at GraphSharp occasionally for over a year but have always discounted it in the past because i couldn’t find any resource on it but you have helped clear the way to do some initial playing with this very interesting but under documented tech!

  4. Mohamed Amine says:

    Hello,

    Thank you for this wonderful tutorial!

    Well I am having a problem with the Graph# CompoundLayout:
    I can’t find how to make the parent vertices(those with childs) display their content correctly, so I need your help.
    Thank you

  5. Nice post. Read & Bookmarked :] Thanks!

  6. Igor Zelaya says:

    Great Article for begginners! Hepled me a lot!

  7. Hiha says:

    Just came across this post and sample code, and I must say, excellent work! This will help me a lot in wrapping my mind around Graph#

  8. pinker says:

    Hi.

    If you want to make an Edge clickable, I would you do?
    (I make the Vertex clickable already).

    Also, if you want to change dinamically the info of a Vertex, how would you make the graph to refresh (for example, if you click on a vertex, swapping from male to female, it should swap also the image based on the trigger)?

    Thanks a lot for this post! It help me a lot.
    regards
    daniela

    • sacha says:

      that’s the sort of question you should ask the graph sharp author over at codeplex. I did not write it

  9. JanekMi says:

    Hi
    I’m pretty new with WPF but I think I found an error in your example. Label’s aren’t displaying edge’s id’s.
    I think error is in this binding:

    , but I have no idea how to fix this. 😀

    If You could fix it and eventually make a suggestion how to change color of the edge accordingly to some edge parameter I would be very grateful. 🙂

  10. GrafX says:

    I mean Text=”{Binding ID}” in style to graphsharp:EdgeControl.

  11. gomino says:

    Hi,

    Thank you for this excellent how-to.
    But there is a problem with your edge tooltip the binding is not working…

    Can you tell me how to fix it ?

  12. Vince says:

    Hi,

    really thanks for the example.
    that’s clear and very useful.

    But now the dl link of the example solution seems down, is it possible to republish it ?

    Thanks to let me know

  13. Gregory Stein says:

    Hi.

    First of all thanks for the great example.

    I’m writing some tool for regex build/debug and one of functionalities of it is to provide a graph of the finite automata it produces. For this purpose I’ve chosen graph#.

    I did some modifications to your in order to make it work with my classes. I have question regarding “EfficientSugiyama” layout algorithm. Is it possible to draw smooth lines instead of elbow lines? I mean some sort of B-Splines?

    Thank you!

    Regards, Greg.

  14. gomino says:

    Problem solved here:
    http://graphsharp.codeplex.com/Thread/View.aspx?ThreadId=70832

    Need the last version of Graphsharp build from source 🙂

  15. sacha says:

    Cool nice one gomino

  16. rame0 says:

    Hi, Sacha!
    Could you help me with one problem:
    I did download your code, but when I run the app, I see only black screen… When I move mouse inside the screen, I can see tooltips, but cant see any UI elements at all 😦

  17. rame0 says:

    Not very good … Ok. I’ll have to ask the developers…

    But still thanks! Good post!

  18. sunny says:

    You are the greatest.

  19. Tuan Nguyen says:

    Dear Sacha!
    It’s great your tutorial. However, it can’t applied for undirected graph. In your code, I replace BidirectionalGraph with UndirectedGraph, and no error, no graph on layout window. Could you explain?
    Thanks a lot!

    • sacha says:

      I am not the author of GraphSharp, so you should ask him at his codeplex site. He should know.

  20. Tuan Nguyen says:

    Thanks Sacha!

  21. sri says:

    Great tutorial, but could be graphsharp used in Silverlight applications? Can you help me? I’m really new in Silverlight. Thank you.

  22. Can says:

    Thank you very much, it is really helpful for us !

    I wonder something about mouse events, when we create a graph we can automatically drag the vertexes or by clicking the empty are we can move the whole graph,but how all of these work ? I want to add multitouch capabilities to graph#, but I don’t know which methods should I call or which events should I fire.

    Can anyone suggest a solution or at least a way for a solution.

    Thanks.

  23. Roland Neubert says:

    I have a question:
    when I replace the GraphSharp.Controls.dll with the complete GraphSharp.Controls project, recompile it and and use that created assembly the the binding graphsharp:EdgeControl…ID does not longer work. Do you have an idea what that can cause or how to debug such a not working binding.

    • sacha says:

      You would need to ask that sort of question to the chap that did GraphSharp which is not me, ask him at codeplex.

  24. Roland Neubert says:

    Thank you for your quick replay – I did that already. What I found for the time being is that the DataItem for the resp. Style changes from DataItem=’PocEdge’ [OK] to DataItem=’MainWindowViewModel’ [faulty].

  25. leng says:

    hi, would like to check if you’ve got any idea on how to add in JSON to query from Freebase, at the same time using GraphSharp to present the layout?

    • sacha says:

      Well thats really a strange question to do with what is essentially how to lay out a graph in WPF. BUt it would involve creating some service that talked to Freebase via JSON objects being returned which you could then get data from and add as vertices and edges, that is all there is to it really.

  26. schorsch says:

    Great tutorial!

  27. Faizan says:

    Hi! I’m new to WPF. Could you please guide me as to how I may get the edges to show data from my custom Edge Type.

    I have weighted graphs, so each Edge type also has an int weight. I would like to show that on the edge…. (not just as a tool tip!)

  28. Faizan says:

    Thanks sacha.
    I know you’re not the author. Its just that you wrote a fantastic tutorial. So I was just looking for pointers.

    Cheers!

  29. whatauser says:

    Hi sacha,
    Do you know if it is possible to use Graph# on an asp.net mvc web application?
    I cant find any guidance about it, and I already asked on codeplex but no one answer.
    Thanks, cheers!

    • sachabarber says:

      Yeah there is no way you could use graph sharp with asp Mvc, no chance. It’s wpf all the way as such not a chance. Hope that clears it up for you

  30. Michael Rinck says:

    Hi Sacha,
    great article! I am planning on displaying a graph that represents versions of documents (with splits and merges and all that). Is it possible to make the vertices linked to the documents so that the user can click them and the document opens?
    Thanks!

    • sachabarber says:

      Sure just have your custom vertex exposé an ICommand which you would bind button to with your custom vertex datatemplate.

      You just need to make sure your custom vertex also has the document URL passed into its constructor. I would recommend using something like josh smiths relaycommand

      • Michael Rinck says:

        Thanks for the quick reply! Do you know of any tutorials showing how to do this? (My knowledge of this is rather basic, as I have not worked with interfaces before)

  31. Hi Sacha,
    I have been fairly successful in rebuilding your example to the point were I have my own customized graph ready to be displayed.
    I am now trying to integrate the WPF solution into my WordAddIn by adding the MWVM class and the appropriate MainWindow.xaml file.
    Now the first thing I added to the MWVM file was:
    public class DDNAGraphLayout : GraphLayout { }
    Before adding the custom Layouttype, everything compiled nicely. As soon as the custom Layouttype is added, I get a very weird error when trying to build:

    Error 3 The “FindRibbons” task failed unexpectedly.
    System.IO.FileNotFoundException: Could not load file or assembly ‘Analyser, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The system cannot find the file specified.
    […]
    Now Analyser is my namespace, that part I understand. Is this error related to me trying to “attach” the graph# solution to my WordAddIn, or am I missing some references?

    Thanks for any help, this really puzzles me.

  32. Joey Kent says:

    Hey Sasha,
    As usual your post are explained beautifully. I have one question related to graphsharp. Can we use GraphSharp to display a huge volume of twitter data (friends/ followers) links ?

    • sachabarber says:

      Thanks. Though for your specific graphsharp question you may need to ask that on graphsharp codeplex site and ask its author, I did not write it

  33. bajajneha27 says:

    Hi Sacha, Thanks for such a nice post. I have done everything you wrote but I am not able to see any graph on my window. Only the background with zoom control is coming up.

  34. Redouane says:

    I heard that it was possible to get only the coordinate of each vertex without drawing it on a WPF application. I saw on the graphsharp forum that I had to use this kind of code to make it, but I only get NaN returns. Do you have any idea ?

    var g = new BidirectionalGraph<VertexControl, IEdge>();
    // some code building the graph

    LayeredTopologicalSortAlgorithm<VertexControl, IEdge> algo = new LayeredTopologicalSortAlgorithm<VertexControl, IEdge>(g);
    algo.Compute();

    var graphLayout = new GraphLayout<VertexControl, IEdge, BidirectionalGraph<VertexControl, IEdge>>();
    graphLayout.Graph = g;

    VertexControl vertex = g.Vertices.ElementAt(1);
    double x = GraphCanvas.GetX(graphLayout.GetVertexControl(vertex)); // NaN
    double y = GraphCanvas.GetY(graphLayout.GetVertexControl(vertex)); // NaN

    • sachabarber says:

      You may have to ask this on the GraphSharp codeplex site. I did not write it, I just posted how to use it here. Sorry

  35. David says:

    Hey i am trying your article and it almost works. I just dont know where to save your data-template. Can you pls explain this to me?

    Thx in advance
    David

  36. Rafał says:

    HI, love this article. It was really helpful with my application, which visualize some algorithms like Dijkstra or A* 🙂
    But i have a little question 🙂
    I wrote my own graph parts, but I’m quite new to WPF, how can i add a label on middle of Edge? I can do Binding and stuff, just can’t figure out how to add it in a first place.
    Thanks upfront 🙂

    • sachabarber says:

      You would have to change the DataTemplate for the edge. This would either be here or in the GraphSharp code

  37. yiqi says:

    Hello,this article is useful. In the end of this article has a link to the source code,is it right?
    I click it, but it show error,maybe the link is missing,can you update that. Thank you in advance

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: