Extending DebuggerVisualizer — visualizing data as a graph in the debugger

Without a doubt, the debugging in Visual Studio is pretty easy to use and powerful. One aspect of it that makes it especially powerful is the ability to extend and customize it to your needs and data. Enter Debugger Visualizers...

Debugger Visualizers are the little pop-up windows that show when you click on the little magnifying glass when inspecting data in debug mode. Such as:


vis1.png


These visualizers are just simply forms that are designed to operate on a specific type of data and present it in a manner that is (hopefully) useful to the developer. Developing these visualizers (or forms) and then registering them with Visual Studio to
use during your debug sessions is the focus of this post.

The Problem

Imagine a type called PointSet which is simply a collection of points, such as:

  1. public sealed class PointSet : List<PointF> {}



Your PointSet might contain thousands of points which is just not practical to inspect item by item in the debugger. Your alternative might be to write out the PointSet set to disk and graph it somehow. Another alternative is to create a debugger visualizer that will take this PointSet and display it in a graph...all within the debugger environment.

The solution

The solution is pretty easy. We just extend DialogDebuggerVisualizer, apply the appropriate assembly decoration, and install it into Visual Studio's Visualizers directory (which is, for 2008
C:\Documents and Settings\YourUserName\My Documents\Visual Studio 2008\Visualizers

Below is PointSetDebugVisualizer, which extends DialogDebuggerVisualizer.

  1. using System;
  2. using System.Diagnostics;
  3. using System.Windows.Forms;
  4. using Microsoft.VisualStudio.DebuggerVisualizers;
  5. using DebugTest;
  6.  
  7. [assembly: DebuggerVisualizer(
  8.             typeof(CustomVisualizers.PointSetDebugVisualizer),
  9.             Target = typeof(DebugTest.PointSet),
  10.             Description = "PointSet visualizer")]
  11.  
  12. namespace CustomVisualizers
  13. {
  14.     public class PointSetDebugVisualizer : DialogDebuggerVisualizer
  15.     {
  16.         public static void ShowTestingWindow(object o)
  17.         {
  18.             VisualizerDevelopmentHost host = new VisualizerDevelopmentHost(
  19.                                         o, typeof(PointSetDebugVisualizer));
  20.             host.ShowVisualizer();
  21.         }
  22.  
  23.         protected override void Show(IDialogVisualizerService windowService,
  24.                         IVisualizerObjectProvider objectProvider)
  25.         {
  26.             PointSet data = (PointSet)objectProvider.GetObject();
  27.  
  28.             using (PointSetGraph form = new PointSetGraph(data))
  29.             {
  30.                 windowService.ShowDialog(form);
  31.             }
  32.         }
  33.     }
  34. }



So really, not a lot going on here.

  • Line 2: Namespace for DebuggerVisualizerAttribute.
  • Line 3: Namespace for DialogDebuggerVisualizer, among other things.
  • Lines 7 - 10: This basically says that the current assembly has a debug visualizer of type CustomVisualizers.PointSetDebugVisualizer (line 9) that operates on type DebugTest.PointSet (line 10), and has the description "PointSet visualizer".
  • Lines 16 - 21: This is just a method for testing that allows the visualizer to be displayed by invoking that method. This method is not required.
  • Lines 23 - 32: The Show() method. This is the meat of the class that will display the visualizer for the given data. This is an overwritten method from DialogDebuggerVisualizer.



The Show() method is displaying a form of type PointSetGraph. This is just a run of the mill form that takes in a PointSet. For the purpose of graphing, I'm using ZedGraph. The code, looks like this:

  1. public partial class PointSetGraph : Form
  2. {
  3.       private PointSet _points;
  4.       public PointSetGraph(PointSet points)
  5.       {
  6.           InitializeComponent();
  7.           _points = points;
  8.       }
  9.       private void PointSetGraph_Load(object sender, EventArgs e)
  10.       {
  11.           _zedGraphControl.GraphPane.Title.Text = "PointSet";
  12.           _zedGraphControl.GraphPane.YAxis.Title.Text = "Y";
  13.           _zedGraphControl.GraphPane.XAxis.Title.Text = "X";
  14.           PointPairList zedPoints = new PointPairList();
  15.           _points.ForEach((x) => zedPoints.Add(x.X, x.Y));
  16.           LineItem curve = _zedGraphControl.GraphPane.AddCurve("Graph", zedPoints, Color.Red, SymbolType.Circle);
  17.           curve.Symbol.Size = 1;
  18.           _zedGraphControl.AxisChange();
  19.       }
  20.       private void PointSetGraph_Resize(object sender, EventArgs e)
  21.       {
  22.           _zedGraphControl.Location = new Point(10, 10);
  23.           _zedGraphControl.Size = new Size(ClientRectangle.Size.Width - 20, ClientRectangle.Height - 20);
  24.       }
  25. }



The above classes are compiled together into their own dll and installed (as stated above, the default visualizers directory is C:\Documents and Settings\YourUserName\My Documents\Visual Studio 2008\Visualizers). Be sure to include any supporting assemblies in your installation. In my case, it was the ZedGraph dll.

The results

In my project that uses a PointSet variable, I get the following result when I hover over a PointSet variable:


vis2.png


Ok, looking good so far. Now clicking on the visualizer gives me:


vis3.png


Looks like a success!

Some tips

Here are some tips to keep in mind when creating a visualizer:

  • The target datatype (in this example it was PointSet) must be serializable.
  • When using generic types, it needs to be concrete. So, List<T> won't work but List<int> will.
  • You can modify the data in the visualizer and then return it back using IVisualizerObjectProvider.ReplaceObject(). In my example, the IVisualizerObjectProvider instance is objectProvider in the Show() method.

kick it on DotNetKicks.com

Leave a Reply