While preparing for an upcoming ODNC Visual Studio TnT (Tips'n'Tricks), SG suggested I talk about VS Debugger Visualizers. hhhmm I heard of them but never really paid them much attention. Man, was I missing out on something HUGE!!!!! Visual Studio's Debugger Visualizers are an easy way to create a custom view on your own class when you're debugging. Yup, you can put your own face on your own custom classes! Now THAT's cool I think!
We create one or two classes a day right? HMPH! you say? Ya, thought so, you're probably creating quite a few and over the course of a week you might have created a hundred (or MORE!). But how do you debug into them? You set a break point and let the default debugger do the rest right? VS' debugger is going to do a ToString() on your objects to give you what you desire. That sounds kind of over simplistic for some complex object that took you all day to create if you ask me! Oh sorry, you didn't ask.....yet that is, I'm sure you're asking it now?! haha
What's the big deal you ask? Well, if you think the Locals, or the Autos or the Watch windows are good enough, then thank you for your time and good bye. So long. BUT, if you think there should be more, if you think you SHOULD be able to tell VS how to display your own classes, then we're on the same brainwave and please continue reading!
Need a bit more context? (Glad you're still here!) Here's an example of what I'm talking about. I whipped up a class called GPSPosition, it has, well, as you can see below, an Address, Description, GoogleMapsLink, Latitude, Longitude and Photo properties (each with appropriately data types). BUT if you/me/anyone using my app would like to put a context around this class.....well.....you'd be SOL unfortunately. Wouldn't it be nice, or even COOL if, as you're debugging you could SEE what the original class designer really intended? Wouldn't it be cool to SEE that Photo as an image? What about seeing the actual Google Maps view? THAT would be cool! If you think so too, then you're at the right place and I hope you keep reading!
Below, I enumerated the different default ways VS gives you to debug into a variable, mainly hovering over the variable, using the QuickWatch window and the using the Autos and Locals windows.
Classic Class Debugging

Quick Watch Window (SHFT+F9)

Debugging With Autos (CTRL+D, A)

Debugging with Locals (CTRL+D, L)

With Visual Studio, you can put "lipstick" on your own classes. A brief overview of what you need to do is, you
- create one form to show off your custom class (Labels, TextBoxes, DataGrids, go nuts!)
- create one class, decorate it with one attribute
- override a Show method
- cast one of the incoming parameters to your custom class
- set the controls on your form
- ShowDialog()
When you do aaaaaaaaaall that, you'll see something similar to this (milage may vary according to number of coffees drank today).


THAT'S IT!!!!!!!!!!! THAT'S ALL THERE IS TO IT! OK, ok, there are some minor gotchas, but they aren't impossible and not too earth shattering.
So, let's start, first off, start VS with admin privs!

Next create a solution, with a Windows Application output type. This is going to be our core piece of code, this is where we'll create our class we want to do some of that fancy "Business Logic" on! LOL This is the class we're going to use to test our upcoming visualizer. Ok, go ahead and create something like this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace PCHenry.ExploreLatLong
{
[Serializable]
public class GPSPosition
{
public double Latitude
{
get;
private set;
}
public double Longitude
{
get;
private set;
}
public string Description
{
get;
private set;
}
public string Address
{
get;
private set;
}
public Image Photo
{
get;
private set;
}
public Uri GoogleMapsLink
{
get
{
return new Uri( string.Format( "http://maps.google.com/maps?q={0},{1}&z=19", Latitude, Longitude ) );
}
}
public GPSPosition( double latitude, double longitude, string description, string address ) :
this( latitude, longitude, description, address, null )
{
}
public GPSPosition( double latitude, double longitude, string description, string address, Image photo )
{
this.Latitude = latitude;
this.Longitude = longitude;
this.Description = description;
if( !string.IsNullOrEmpty( address ) )
{
this.Address = address;
}
if( photo != null )
{
this.Photo = photo;
}
}
public override string ToString()
{
return string.Format( "{0} @ \t({1}, {2})", Description, Latitude, Longitude );
}
}
}
Make sure you put the [Serializable] attribute as the class decorator. If you forget, weeeeeeeell, that's ok, cause the compiler will remind you! HAHA
Next, design the main application's form. Mine looks like this (ain't it pertty? hey, I never claimed to be a graphics designer!). LOL

OK, at this point, compile it, debug it, run it, ooops, test it and change it, run, test, change, compile, rinse, repeat. Let's do this the old fashioned way. Hey, it's the last time you're going to do this, so let's just do it for old time sake ok? DOH!
Now we're going to add our "Debugger Visualizer" part, the really cool part.
Add a new project to your solution, but this time add an output type of Class Library. We want this to be a DLL. This DLL is going to be copied to a special place so Visual Studio can see and use it, but let's not get too far ahead of ourselves yet.
Add a Windows Form to your Class Library project (example below is what I'm using). I know, I know, it looks an aweful lot like the one above, just add a bit of flavour to yours (I added the ability to click the image to launch the actual URL in a browser).

The key to this new class is to create a constructor that takes an object that you juuuuuuuust created above in the other project. Yup, you'll need to add a reference to your other project (which is why we did that one first). Next, while we're adding in references, add in Microsoft.VisualStudio.DebuggerVisualizers. We'll need that in the next step.

Next is where the REAL magic happens (ya, ya, about time, I know, I know, all that other stuff was to make this anti-climatic, and just work). Add a new class to your class library, and this time, extend the DialogDebuggerVisualizer class. Next, add in a DebuggerVisualizer attribute (details coming), and then override the Show method. In other words.....
using System;
using System.Diagnostics;
using Microsoft.VisualStudio.DebuggerVisualizers;
using PCHenry.ExploreLatLong;
[assembly: DebuggerVisualizer( typeof( PCHenry.Debugger.Visualizer.GPSPositionVisualizer ),
typeof( VisualizerObjectSource ),
Target = typeof( PCHenry.ExploreLatLong.GPSPosition ),
Description = "GPSPosition Debugger Visualizer" )]
namespace PCHenry.Debugger.Visualizer
{
class GPSPositionVisualizer : Microsoft.VisualStudio.DebuggerVisualizers.DialogDebuggerVisualizer
{
protected override void Show( Microsoft.VisualStudio.DebuggerVisualizers.IDialogVisualizerService windowService,
Microsoft.VisualStudio.DebuggerVisualizers.IVisualizerObjectProvider objectProvider )
{
GPSPosition gpsLocation = (GPSPosition)objectProvider.GetObject();
GPSPositionForm frm = new GPSPositionForm( gpsLocation );
frm.ShowDialog();
}
}
}
Notice the assembly attribute at the top right? This is what helps Visual Studio figure out when to use THIS visualizer for debugging. The first parameter is yourself (this class), next is the VisualizerObjectSource and is common everytime you'll use this, next is the target type of object you're using this visualizer/Windows Form to display "lipstick" for, and lastly the english (or your own language of choice) for the magnifying class to display for selecting this visualizer (this is the text displayed above by the number 3 arrow).
The next thing is the override the Show method. Use the IDialogVisualizerService parameter to give you the ShowDialog() method and the IVisualizerObjectProvider parameter to give you the actual object (GPSPosition in this case) to debug into. As I hope you can see, IVisualizerObjectProvider is an object and therefore requires casting. The more observant coder (SG?) might comment about using "as", so here goes an "alternative ending." (it is Halloween afterall and alternative endings seem to be common on horror movies)
protected override void Show( IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider )
{
GPSPositionForm frm;
//GPSPosition gpsLocation = (GPSPosition)objectProvider.GetObject();
GPSPosition gpsLocation = objectProvider.GetObject() as GPSPosition;
if( gpsLocation != null )
{
frm = new GPSPositionForm( gpsLocation );
}
else
{
frm = new GPSPositionForm( new GPSPosition( 0, 0, "Unable to determine GPS latitude or longitude", "NA" ) );
}
windowService.ShowDialog( frm );
}
Ok, at this point, we can compile our code and create the DLL. Beautiful! But now we need to deploy this so Visual Studio can use it. To deploy this, you'll need to copy the DLL to the C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Packages\Debugger\Visualizers directory. Notice the (x86) in the directory structure? If you're using x86 Windows, just ignore that part, the rest is the same. If you copy this over correctly, you'll see something similar to this.

At this point, you're ready to test this out. Set a break point in your first application on the first line AFTER you run the SelectedIndexChanged event. Right mouse click the locationToShow variable and select the magnifying glass to run your Visualizer or the down arrow to select from the options (if there are multiple Visualizers).
If you get an error that looks like this, then obviously something's gone wrong with the code, DOH! HHMMM a bug with the debugger code? Yup, ironic isn't it? Or is that recursive? ANYWAYS, just dig into it and you'll eventually figure it out. HINT, WATCH OUT FOR DB CALLS! Don't forget, you're in something like a static class state, and you don't necessarily have everything you need or want. If you want DB access, you'll probably have to carry an application.config around (and keep it up to date too!). So be careful.

If you're thinking to yourself..........are we done yet? If you're ok with manually copying the DLLs over to the Visualizers directory, then ya, you're done! Enjoy! If not, then read on for just a short bit. Visual Studio has a small ability to run script-like code after builds/compiles. Let's try to use this capability to get VS to copy over our DLL instead of us doing it all the time.
Goto the Visualizers Class Library's Project Preferences and select the Build Events tab on the left hand side.

In the Post-build event, I would suggest adding this line. This is the MS-DOS xcopy command to copy the deliverables (namely your custom Visualizer DLL) to the Visualizers directory (don't forget to change the (x86) from this path if you're not using a 64-bit machine). Make sure you select the bottom listbox correctly for your preference.
xcopy "$(SolutionDir)$(ProjectName)\$(OutDir)$(TargetFileName)" "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Packages\Debugger\Visualizers" /y
If you're asking what are the $(text) strings above? Those are macros and I would HIGHLY suggest using them to help minimize the "it works on my machine, I don't have any clue why it's not working on yours?" As you can see from the above first few macros, it doesn't matter where my coworkers/friend's/YOU compile this, I'm pretty confident this piece will work on your machine because of it (except for the (x86) part that is).
Something to watch out for when using Post-build events, if for some reason, ANY reason, VS cannot run those build events, chances are high you'll see the problems manifest themselves as Errors in your builds. The description will tell you where the error was, but it won't be anything linked to your code, nor will it be very helpful (VS can only report back what the command line functions report back). So! For example, IF you didn't heed my warnings above and start VS with Administrative Privileges, you will be finding out riiiiiiiiiiiiiiiiiiiiiight about now that you're getting errors when you try to xcopy the files over to the Visualizers directory because you don't have permissions and xcopy isn't going to ask you for permissions, it'll just fail. So if you're in this boat, restart VS but this time, start with Admin privs please?!
Well, there you have it. Your own way to customize debugging your own classes! Now you can put a happy face on all your custom classes and show them off for all their glory. Now it's time to grab a coffee and get coding!
NOTE: Unfortunately there hasn't been too many 2008 focused exampes of creating these visualizers, but thankfully, the same stuff you did with 05 works with 08! Cool! The crux now is not necessarily the IDE but the OS! Yup, the OS, Windows! Back with VS 05, UAC wasn't around and causing people troubles. This time around it is and although you can get away without running VS with Administrator privileges, it makes your life a bit easier. Why? Cause part of "installing" or deploying your Debugger Visualizer is to copy the DLL files to a Program Files sub folder and if you run VS normally you won't be able to use VS to do that automagically (we're going to use VS to automate this step for us).
Resources:
Source Code: http://www.pchenry.com:8080/svn/blog/trunk/2009/DebuggingVisualizer
MSDN: Visual Studio Debugger Visualizers
MSDN: Creating a Debugger Visualizer Using Visual Studio 2005 Beta 2
MSDN: DebuggerVisualizerAttribute Class
The Code Project: Authoring Visual Studio Debugger Visualizers
CODE: Creating Debugger Visualizers with Visual Studio 2005
Alex Pinsker: Comprehensive list of Debugger Visualizers for Visual Studio
Google Maps: Google Map Parameters
QueryStringParameters: Google Maps Query String Parameters
Microsoft Technet: Xcopy