Click here to Skip to main content
15,879,068 members
Articles / Desktop Programming / WPF

Dot2WPF - A WPF Control for Viewing Dot Graphs

Rate me:
Please Sign up or sign in to vote.
4.99/5 (35 votes)
13 Jun 2007CPOL8 min read 208.5K   6.1K   112   40
A fast and smooth WPF viewer for graphs layouted by GraphViz (Dot)
This article shows you how easy it is to do "owner drawn graphics" in WPF. The viewer of this sample also has some advantages over existing viewers.

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 was 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:

XML
<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.

C#
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.
  • There are 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

  • 21st May, 2007 -- 0.2.0.0, First public release
  • 13th June, 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)


Written By
Software Developer
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, Prolog, OML, C, C++, C#, Java, Scala, Groovy, Clojure, VB, Eiffel, Delphi, Perl, Pascal, Javascript). Currently my favorite languages are Clojure, Ruby and modern Javascript.

Comments and Discussions

 
QuestionChiristian, Is it possible to have CONCEPT OF TOKENS flowing through DOT GRAPH like animation in BPMN FLOW Pin
Member 1333892125-Jun-18 17:49
Member 1333892125-Jun-18 17:49 
Questionbackground map Pin
jianyunli20-Mar-14 8:58
jianyunli20-Mar-14 8:58 
QuestionFork created on Blade.Net Pin
Member 408378310-Feb-14 1:02
Member 408378310-Feb-14 1:02 
GeneralMy vote of 5 Pin
mmike7429-Dec-10 6:08
mmike7429-Dec-10 6:08 
Generalarrow head directions Pin
HHick123453-Jun-10 22:12
HHick123453-Jun-10 22:12 
Generalno scroll wheel on laptops Pin
HHick1234531-May-10 5:30
HHick1234531-May-10 5:30 
Generalplain file compatibility Pin
HHick1234531-May-10 5:03
HHick1234531-May-10 5:03 
GeneralGreat work!! Pin
HHick1234510-May-10 23:31
HHick1234510-May-10 23:31 
GeneralRe: Great work!! Pin
HHick123453-Jun-10 21:54
HHick123453-Jun-10 21:54 
Generalanother quick question.. Pin
Mansoorahmed8614-Oct-09 23:52
Mansoorahmed8614-Oct-09 23:52 
GeneralRe: another quick question.. Pin
Christian Rodemeyer18-Oct-09 11:50
professionalChristian Rodemeyer18-Oct-09 11:50 
GeneralRe: another quick question.. Pin
Mansoorahmed8619-Oct-09 6:27
Mansoorahmed8619-Oct-09 6:27 
QuestionCan we associte any events with the nodes and edges? Pin
bobby2918-Aug-08 6:57
bobby2918-Aug-08 6:57 
GeneralReposition nodes Pin
sorechops12-Jul-08 1:38
sorechops12-Jul-08 1:38 
GeneralSilverlight and the multiscaleimage control Pin
Kent K12-Jun-08 9:30
professionalKent K12-Jun-08 9:30 
QuestionCan this be used in a Web based Application Pin
arbrunda13-Dec-07 18:08
arbrunda13-Dec-07 18:08 
GeneralHelp in graphics programming needed !! Pin
Lalito8019-Jun-07 5:03
Lalito8019-Jun-07 5:03 
GeneralRe: Help in graphics programming needed !! Pin
Manas_Patnaik21-Jun-08 0:19
Manas_Patnaik21-Jun-08 0:19 
QuestionTWOPI? Pin
yeatd15-Jun-07 9:41
yeatd15-Jun-07 9:41 
AnswerRe: TWOPI? Pin
Christian Rodemeyer16-Jun-07 11:20
professionalChristian Rodemeyer16-Jun-07 11:20 
Hi,

thank you for testing TWOPI. It should be no problem to view graphs rendered by TWOPI, but it seems that you have found a couple of bugs.
I rendered the twopi2.dot.txt file with twopi into twopi.plain on my machine, viewed it and saw every node as solid Frown | :( I think this what you mean when you say "flat structure"
I analyzed my code and found three issues:
1. There are a lot of "...grey" colors in the .dot file. Unfortunately, WPF spells the the "...grey" colors as "...gray". I can fix this through replacing "grey" with "gray" in GraphLoader's GetCachedBrush method:
color = color.ToLowerInvariant().Replace("grey", "gray");
2. If an edge has a label, than I don't read the color correctly
3. The .plain output of TWOPI confuses the fillcolor with the bordercolor. The dot user manual (Appendix B) says that it should be the last string in a line, but it isn't. For this case you can change Line 107 in GraphLoader from parser.Read(3) to parser.Read(2).

I will investigate further and see if this problem also exists in annotated dot.

Thank you again for finding these issues.

Christian
---
Always expect the unexpected!

GeneralRe: TWOPI? Pin
yeatd17-Jun-07 6:15
yeatd17-Jun-07 6:15 
GeneralRe: TWOPI? Pin
Christian Rodemeyer17-Jun-07 10:25
professionalChristian Rodemeyer17-Jun-07 10:25 
QuestionLicense? Pin
mikekreuzer6-Jun-07 22:59
mikekreuzer6-Jun-07 22:59 
AnswerRe: License? Pin
Christian Rodemeyer9-Jun-07 3:18
professionalChristian Rodemeyer9-Jun-07 3:18 
GeneralRe: License? Pin
mikekreuzer10-Jun-07 16:15
mikekreuzer10-Jun-07 16:15 

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

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