Click here to Skip to main content
11,438,797 members (75,740 online)
Click here to Skip to main content

Dot2WPF - a WPF control for viewing Dot graphs

, 13 Jun 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
A fast and smooth WPF viewer for graphs layouted by GraphViz (Dot)

Screenshot - dot2wpf_small.png

Screenshot - dot2wpf_zoomed.png

Introduction

I am presenting a WPF control for viewing graphs rendered by GraphViz (Dot). The DotViewer control has the usual navigation -- i.e. zoom, drag, scroll -- and supports hit testing on nodes, which is used for displaying tooltips. In the first place, this article should show you how easy it is to do "owner drawn graphics" in WPF. As it turns out, the viewer of this sample also has some advantages over existing viewers:

  • It is several times faster, probably because WPF is hardware accelerated
  • Because WPF works vector-oriented, zooming is fast and produces smooth, good looking pictures
  • Nodes can be found by mouse position, which makes user interactions possible (tooltips, selections)
  • You can easily integrate it into your own .NET applications
  • It works with huge graphs; for example 350 nodes with 2600 edges -- i.e. more than 53000 Bezier points -- can be displayed and zoomed nearly delay-free

I suggest that you now download the sample and play with it a little before continuing.

Motivation

Currently, I am working on a project that is packaged in over 500 assemblies. To understand this packaging better, I wrote a little python script that analyses the assembly dependencies and generates a graph description for GraphViz. It uses Dot to render a GIF image and displays it with the standard windows image viewer. This works well for small graphs. The first time I tried to view the graph of the complete system, however, I got images so big -- more than 80000 pixels wide -- that it took minutes to load, display, zoom and scroll them. I tried other viewers but wasn't really satisfied due to their speed, usability and the possibility of using them in my application. Also, the printing capabilities are very limited. I found no easy way to print large graphs on several pages. So for a while I viewed only subgraphs, arranged for my current needs and never seeing the big picture. Then I remembered TechEd 2006 in Barcelona, where I saw some really impressive WPF demos. Instantly, I knew: this was the right problem to try WPF with. If it is really so cool, as Microsoft emphasized, it should be no problem to make my own lightning-fast viewer. And lo and behold: Microsoft were right!

Background

  • GraphViz Homepage - a very good and free tool for graph visualization, used as the layout engine for this project
  • MSDN DrawingVisual sample - this gave me a quick start into using visuals for high performance drawing

Alternatives

  • QuickGraph - a graph library with additional GDI+ based GraphViz support
  • Glee - a Microsoft Research project that does the layout and rendering of graphs, also based on GDI+

Sample application

The solution contains two projects. The Visualizing project contains the DotViewer control. The Dot2Wpf project is just a simple wrapper application that hosts the DotViewer control. It allows you to open files in the .plain format produced by Dot. Dot2Wpf contains several samples, so you don't need to install the GraphViz package if you just want to play around a bit. If you have GraphViz installed, you can create the .plain output from .dot files with this command:

dot -Tplain -o "graph.plain" "graph.dot"

You can open a file by pressing Ctrl+O or by clicking the button in the upper left. Use the mouse wheel to zoom the graph. If the graph gets too big, move it with the scrollbars or drag it with the right mouse button. You can select a node by clicking it. If you hover the mouse over a node, it will display a tooltip. Just in case you are wondering about the meaning of my graph samples:

  • The node color indicates the area from and to which the assembly is assigned
  • The size of the node text is proportional to the code size of the assembly
  • Orange edges indicate dependencies that are defined at compile time, but not used at runtime

The DotViewer control

The DotViewer control is contained in the Visualizing project. It is a simple UserControl composed of a standard ScrollViewer and some floating TextBlocks. The ScrollViewer itself contains the GraphElement, which is derived from FrameworkElement and is my host for the visuals that draw the entire graph. You can use the DotViewer in your own applications by simply adding it to a panel. If you are using XAML, you probably want to define a custom namespace that allows you to write something like the following:

<Window xmlns:r=
    "clr-namespace:Rodemeyer.Visualizing;assembly=Rodemeyer.Visualizing"
[...]
<Grid>
    <r:DotViewer x:Name="MyDotViewer"></r:DotViewer>
</Grid>

After the control has been loaded -- wait for the Loaded event of the hosting window -- you can call LoadPlain to load a .plain graph file. If you want to supply tooltips for nodes, you have to subscribe the ShowNodeTip event. NodeTipEventArgs has a Tag attribute that identifies the node. It is the nodeID from the .dot file. Assign the Content attribute your tooltip content. It can be arbitrary WPF content, but most probably you will use a TextBlock element.

The GraphElement uses the GraphLoader class to read the .plain output of Dot and create the visuals displaying the graph. Because shapes are not very efficient when there are many of them, I am using visuals. Visuals don't support high-end stuff like data binding triggers, but they are very performant and still have the ability to do hit testing via VisualTreeHelper. The graph is represented by a DrawingVisual with children. It directly contains all edges. Every node is a child DrawingVisual, tagged with the nodeID from the original .dot file. This is necessary to distinguish between the nodes when hit testing.

Printing and paginating

Graphs are frequently huge and if you print them on one page, the text is often unreadably small. With GraphViz, I had real problems with printing big graphs. I had to render into the PS format and used Adobe Distiller to manually distribute the output over several pages. This was a very time-consuming process. WPF uses a DocumentPaginator in its PrintDocument method and I hoped I could use this class to do my own paginating.

In reality, DocumentPaginator is just an abstract class that does nothing. But by overriding the GetPage method and the PageCount property, I was able to print my graph visual on several pages. The constructor of my GraphPaginator class gets the visual and the size of one printed page. The first problem I needed to solve was getting a copy of the original visual. I found no way to do this, so I created a new visual and used the DrawDrawing to draw the Drawing properties of each visual. My new visual could now be transformed and clipped as I wanted, without changing the original visual. All that the GetPage method now had to do was translate to the proper page position and clip everything that didn't belong to this page. Because I wanted to draw glue marks on every page, I used the same trick as before and created a new visual. I drew the glue marks on it and then the clipped part of the graph that I needed.

public override DocumentPage GetPage(int pageNumber)
{
    int x = pageNumber % pageCountX;
    int y = pageNumber / pageCountX;

    Rect view = new Rect();
    view.X = x * contentSize.Width;
    view.Y = y * contentSize.Height;
    view.Size = contentSize;

    DrawingVisual v = new DrawingVisual();
    using (DrawingContext dc = v.RenderOpen())
    {
        dc.DrawRectangle(null, framePen, frameRect);
        dc.PushTransform(
            new TranslateTransform(margin - view.X, margin - view.Y));
        dc.PushClip(new RectangleGeometry(view));
        dc.DrawDrawing(graph);
    }
    return new DocumentPage(v, PageSize, frameRect, frameRect); 
}

Points of interest

  • I had to implement my own ToolTip service because the standard service allows only one tooltip per UIElement. Because only one UIElement (GraphElement) is responsible for rendering the complete graph, the standard ToolTipService was not suitable.
  • With WPF Bezier methods, it was extremely easy to render the Dot output, just a few dozen lines of code. In fact, the most difficult part was to draw the arrowheads. I had to normalize a vector, rotate it and then scale it a bit to get the desired effect. Thank God WPF has a Vector class at last!
  • Most WPF books tell you that you need a HostElement to display a DrawingVisual. This is true, but you need to do your own layout code in MeasureOverride and ArrangeOverride. Without doing so, WPF doesn't know how big your element is and your element probably won't behave as you expect!
  • I tried to rotate the visual before printing at 90 degrees to do my own landscape orientation. However, I got some very ugly text output as a result. Printing to the XPS printer was fine and as expected, but printing to a real (PCL) printer made garbage of my text. So I override the Page orientation, regardless of what the user selects. This also works on real printers.

Limits

Currently, DotViewer supports only the .plain output format of Dot. This means that:

  • Every node is rendered as an ellipse
  • All edges are interpreted as arrows
  • No edge labels
  • The font is hard-coded as Verdana, so if you want another you will have to change this in the code

Future

I want to relax the limits of the .plain format and switch to annotated Dot. I hope that this will allow me to render any valid .dot graph.

History

  • May 21, 2007 -- 0.2.0.0, first public release
  • June 13, 2007 -- 0.3.0.0, added print support

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Christian Rodemeyer
Architect AutoScout24 GmbH
Germany Germany
I'm developing for fun since 1985, starting with UCSD Pascal on some old machines (no hard disk, but four floppies!), then moving quickly on to assembler on the famous C64 and Amiga. During university I started professional development for Windows/Unix/Linux, using a myriad of languages (Pi, Assembler (6502, 68000, 80386/486), Cobol, Modula2, OML, C, C++, C#, Java, Scala, Clojure VB, Prolog, Eiffel, Delphi, Perl, Pascal). Currently my favorite languages are Clojure and Ruby.

Comments and Discussions

 
Questionbackground map Pin
jianyunli20-Mar-14 9:58
memberjianyunli20-Mar-14 9:58 
QuestionFork created on Blade.Net Pin
Member 408378310-Feb-14 2:02
memberMember 408378310-Feb-14 2:02 
GeneralMy vote of 5 Pin
mmike7429-Dec-10 7:08
membermmike7429-Dec-10 7:08 
Generalarrow head directions Pin
HHick123453-Jun-10 23:12
memberHHick123453-Jun-10 23:12 
Generalno scroll wheel on laptops Pin
HHick1234531-May-10 6:30
memberHHick1234531-May-10 6:30 
Generalplain file compatibility Pin
HHick1234531-May-10 6:03
memberHHick1234531-May-10 6:03 
GeneralGreat work!! Pin
HHick1234511-May-10 0:31
memberHHick1234511-May-10 0:31 
GeneralRe: Great work!! Pin
HHick123453-Jun-10 22:54
memberHHick123453-Jun-10 22:54 
Generalanother quick question.. Pin
Mansur Ehmad15-Oct-09 0:52
memberMansur Ehmad15-Oct-09 0:52 
GeneralRe: another quick question.. Pin
Christian Rodemeyer18-Oct-09 12:50
memberChristian Rodemeyer18-Oct-09 12:50 
GeneralRe: another quick question.. Pin
Mansur Ehmad19-Oct-09 7:27
memberMansur Ehmad19-Oct-09 7:27 
QuestionCan we associte any events with the nodes and edges? Pin
bobby2918-Aug-08 7:57
memberbobby2918-Aug-08 7:57 
GeneralReposition nodes Pin
sorechops12-Jul-08 2:38
membersorechops12-Jul-08 2:38 
GeneralSilverlight and the multiscaleimage control Pin
Kent K12-Jun-08 10:30
memberKent K12-Jun-08 10:30 
QuestionCan this be used in a Web based Application Pin
arbrunda13-Dec-07 19:08
memberarbrunda13-Dec-07 19:08 
GeneralHelp in graphics programming needed !! Pin
Lalito8019-Jun-07 6:03
memberLalito8019-Jun-07 6:03 
GeneralRe: Help in graphics programming needed !! Pin
manas21-Jun-08 1:19
membermanas21-Jun-08 1:19 
QuestionTWOPI? Pin
yeatd15-Jun-07 10:41
memberyeatd15-Jun-07 10:41 
AnswerRe: TWOPI? Pin
Christian Rodemeyer16-Jun-07 12:20
memberChristian Rodemeyer16-Jun-07 12:20 
GeneralRe: TWOPI? Pin
yeatd17-Jun-07 7:15
memberyeatd17-Jun-07 7:15 
GeneralRe: TWOPI? Pin
Christian Rodemeyer17-Jun-07 11:25
memberChristian Rodemeyer17-Jun-07 11:25 
QuestionLicense? Pin
mikekreuzer6-Jun-07 23:59
membermikekreuzer6-Jun-07 23:59 
AnswerRe: License? Pin
Christian Rodemeyer9-Jun-07 4:18
memberChristian Rodemeyer9-Jun-07 4:18 
GeneralRe: License? Pin
mikekreuzer10-Jun-07 17:15
membermikekreuzer10-Jun-07 17:15 
Questionhow to navigate (zoom/drag)? Pin
MacyChin28-May-07 23:10
memberMacyChin28-May-07 23:10 
AnswerRe: how to navigate (zoom/drag)? Pin
Christian Rodemeyer29-May-07 4:27
memberChristian Rodemeyer29-May-07 4:27 
GeneralGraphViz DotViewer for .net 2.0 Pin
latticesoft25-May-07 13:06
memberlatticesoft25-May-07 13:06 
GeneralRe: GraphViz DotViewer for .net 2.0 Pin
Ivan Ferrer22-Nov-10 14:30
memberIvan Ferrer22-Nov-10 14:30 
GeneralA quick question Pin
Sacha Barber22-May-07 22:45
memberSacha Barber22-May-07 22:45 
GeneralRe: A quick question Pin
Christian Rodemeyer23-May-07 1:01
memberChristian Rodemeyer23-May-07 1:01 
GeneralRe: A quick question Pin
Sacha Barber23-May-07 2:06
memberSacha Barber23-May-07 2:06 
GeneralCurious about something Pin
Sacha Barber22-May-07 2:12
memberSacha Barber22-May-07 2:12 
GeneralRe: Curious about something Pin
Christian Rodemeyer23-May-07 0:39
memberChristian Rodemeyer23-May-07 0:39 
GeneralRe: Curious about something Pin
Sacha Barber23-May-07 2:07
memberSacha Barber23-May-07 2:07 
GeneralSuggestion... Pin
Schmoo2k22-May-07 0:02
memberSchmoo2k22-May-07 0:02 
GeneralGood Pin
norm .net21-May-07 21:35
membernorm .net21-May-07 21:35 
GeneralError running demo Pin
Herbert Sauro21-May-07 18:31
memberHerbert Sauro21-May-07 18:31 
GeneralProblem solved. Pin
Herbert Sauro21-May-07 18:34
memberHerbert Sauro21-May-07 18:34 
GeneralRe: Error running demo Pin
Irwan Hassan29-May-07 23:57
memberIrwan Hassan29-May-07 23:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150506.1 | Last Updated 13 Jun 2007
Article Copyright 2007 by Christian Rodemeyer
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid