Foreword
This article presents a debugger visualizer for WPF called Woodstock. After Woodstock was released to the world, it was used as a prototype to design and develop a superior visualizer called Mole v4 For Visual Studio - With Editing. We recommend that you use Mole v4 For Visual Studio - With Editing as your primary WPF and Visual Studio visualizer, because its functionality is a superset of what Woodstock has to offer. However, if you are interested in learning about the evolution of Mole by seeing what came before it, be sure to read this article.
Table of Contents
This article presents a Visual Studio debugger visualizer called "Woodstock" which enables you to view the visual tree. It provides detailed information about all properties on every element in the visual tree, and a snapshot image of each element, allowing you to more easily debug complex WPF user interfaces.
Every WPF developer knows and loves Snoop, the powerful free utility created by Pete Blois. Amongst other things, Snoop allows you to view the visual tree of any WPF application, and inspect the properties of any element. This is a huge time-saver, and a great way to learn about WPF.
Despite the tremendous powers of Snoop, it does not really mesh well with debugging WPF code in Visual Studio. Snoop is a separate application which attaches itself to a WPF application's process. You cannot step through WPF code and use Snoop on that UI at the same time. I, Josh, often found myself debugging some WPF code and wishing that Snoop somehow worked in Visual Studio. That's why Woodstock was born.
I created a small subset of Snoop's functionality, and implemented it as a debugger visualizer. Since the uber-visual tree inspection application is called "Snoop", I decided to name my little visualizer "Woodstock". In case you are not familiar with Peanuts, here's the lowdown on Woodstock.
Throughout the article, when you see the word "I", it is referring to me, Josh Smith. I created Woodstock and wrote this article about it. The burden of updating the code, and this article, falls squarely on my shoulders. Woodstock is my invention, but it would not be nearly as useful and efficient without the numerous suggestions and bug reports I received from dozens of people across the globe!
Karl Shifflett has been a huge help in making Woodstock a great debugging tool. Ever since I published the initial article, he has made many great suggestions, sent me code snippets, reported bugs, etc. To thank him and show my appreciation, I decided to add him as an author of this article.
Shortly after this article was published, Florian Kruesch published an article about a visualizer which shows you an image of the element you're inspecting in the debugger. We agreed to merge his visualizer and article into Woodstock. Karl and I changed his code a bit so that it would work well in Woodstock, but the initial idea of an image visualization came from Florian.
If you just want to download the visualizer and install it, follow these steps:
- At the top of this article, click the "Download the visualizer" link.
- Save the ZIP file to disk and extract it.
- Place "JoshSmith.WpfVisualizer.Woodstock.dll" at one of these locations:
- VS install path\Common7\Packages\Debugger\Visualizers
- My Documents\Visual Studio 2005\Visualizers
Instead, if you click the "Download the code and demo" link, you will get the Woodstock.zip file, in which there are two Visual Studio solutions. The one called "WpfVisualizer" contains the Woodstock visualizer code. The other, "WpfVisualizerTestApp", is a demo app which allows you to try out Woodstock. Be sure to drop the visualizer DLL into your Visualizers directory first before running the test app.
If you have any trouble installing the visualizer, there is a bleak amount of documentation on how to do so here.
If you are using this visualizer in Visual Studio 2005, then be sure to download the DLL which was compiled for VS2005. It turns out that the Microsoft.VisualStudio.DebuggerVisualizers.DLL assembly has a different version for VS2008, so I made a separate build of Woodstock for VS2008 users. The source code and demo app are compiled against VS2005 with the Orcas extensions, so that people who have not yet switched to VS2008 can use it too.
However, if you decide to build Woodstock in VS2008, be sure to remove the reference to Microsoft.VisualStudio.DebuggerVisualizers.DLL and add a reference to version 9.0.0.0 of that assembly. If you do not, you will get a nasty exception when trying to use Woodstock.
One big advantage Woodstock has over Snoop is that you can use it to debug XBAP applications. Here is what you need to do to enable that:
- Right-click on the XBAP project in Solution Explorer and open the Properties page.
- Under the Security tab, select the "This is a full trust application" radiobutton.
- Be sure to set it back later on to partial trust in order to properly test deployment.
Thanks to CodeProject member "ivolved" for that tip!
You can use Woodstock just like any other debugger visualizer. Here's what the datatip looks like when you mouse over a DependencyObject
-derived object after hitting a breakpoint:

When you open the Woodstock visualizer, it looks like this:

The TreeView
on the left represents the entire visual tree of the UI you are debugging. Initially, the element which you hovered the mouse over in the code editor will be selected in the TreeView
, but you are free to select any element in the visual tree. If an element has its Name
property set, then that name will appear next to the element's type. Each element that has descendants has the number of descendants displayed beside it in parentheses.
The DataGridView
on the right shows all of the properties of the selected element. If a property is a dependency property, then the "Value Source" column shows what is providing the effective value of that property (i.e., is it a locally provided value? a value provided by a template? a Style? a system theme? etc). Having the source of a dependency property's value can make it much easier to track down issues where one property might be set from any number of external influences (which is the heart and soul of DPs).
Clicking on a button to the left of a property name will copy a Google search query to your clipboard, so that you can paste that URL into a Web browser's address box and research the property. I wanted to just open a Web browser to that URL, but attempting to do so always made Visual Studio crash when running on Windows XP (but, oddly enough, not Vista).
You can filter the properties shown in the grid by typing a case insensitive filter string into the TextBox
toward the bottom of the Form
. If you do not want to view the attached properties, simply uncheck the CheckBox
next to the TextBox
. If you only want to view attached properties, leave the CheckBox
checked and type "." as your filter text string. ;)
When you click on the "Selected Element Snapshot" tab, you will see an image of the element currently selected in the TreeView
. Here's a screenshot of that:

UIElement
s and FrameworkElement
s must be initialized and loaded before they can be displayed. As a result, when you put a breakpoint in, for example, a Window
's constructor, you won't see anything if you check out the visuals in that Window
.
In case you have never used a debugger visualizer before and want some more information, read this page in the docs.
There is one issue which might possibly make using Woodstock difficult. Visual Studio's "Debugger Visualizer" feature only allows a visualizer to have a brief amount of time to serialize its data. If your visual tree is huge, or your machine is bogged down processing other things, etc., then Woodstock might take too long and it will time out. Visual Studio will show an error dialog stating: "Function evaluation timed out."
I did my best to work around this limitation by optimizing the way that information about the visual tree is retrieved and represented. Before making the optimizations, I tested Woodstock against an application with a gigantic visual tree and long-running background processes, and it timed out frequently. After making the optimizations, it never timed out. Hopefully, those optimizations will prevent you from seeing that annoying error message, too.
Debugger visualizers are strange little creatures. You have to keep in mind that there are two logical parts involved: debugger-side code and debuggee-side code. Visual Studio's debugger allows you to inject some code into itself and the process being debugged. The code in the debugger process shows the visualizer UI. The code in the process being debugged allows you to package up the data that you want to display in the visualizer. Once that data has been serialized, it gets shipped over to your visualizer in the debugger process, at which point you can turn it back into live objects and display them. For more information about the architecture of visualizers, read this article in the SDK.
A visualizer must specify a class for which it provides visualizations, including subclasses of that class. We want our visualizer to work for any DependencyObject
, or subclass thereof. The problem is that DependencyObject
is not serializable, so we cannot rely on the standard serialization behavior of the debugger visualizer framework to make life nice and easy for us. Instead, I created a class which intercepts requests to serialize the visual tree, and then created a parallel hierarchy of serializable objects which contain the visual tree information we want to display in the visualizer. Here is that class:
public class ElementTreeVisualizerObjectSource : VisualizerObjectSource
{
public override void GetData(object target, Stream outgoingData)
{
DependencyObject depObj = target as DependencyObject;
WpfElementTree elementTree = new WpfElementTree(depObj);
_binaryFormatter().Serialize(outgoingData, elementTree);
}
}
The visualizer itself is a simple class which only shows a custom WinForms Form
, as seen below:
public class ElementTreeVisualizer : DialogDebuggerVisualizer
{
protected override void Show(IDialogVisualizerService windowService,
IVisualizerObjectProvider objectProvider)
{
using (Form displayForm = new ElementTreeVisualizerForm(objectProvider))
windowService.ShowDialog(displayForm);
}
}
In the AssemblyInfo.cs file, an attribute is applied which Visual Studio uses to figure out what type(s) this visualizer is used for, as well as some other information. That attribute is seen below:
[assembly: System.Diagnostics.DebuggerVisualizer(
typeof(WpfVisualizer.ElementTreeVisualizer),
typeof(WpfVisualizer.ElementTreeVisualizerObjectSource),
Target = typeof(System.Windows.DependencyObject),
Description = "Visual Tree Visualizer (Woodstock)")]
One piece of the code which took me (Josh) a while to get right was the logic which stores information about all of an element's properties. Information about a property is stored in an instance of my WpfElementProperty
struct. A WpfElement
object has a WpfElementPropertyList
which stores information about every property of that element. The logic which populates that list is seen below:
private void Initialize(DependencyObject propertySource)
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(propertySource);
foreach (PropertyDescriptor prop in props)
{
PropertyDescriptor resolvedProp = prop;
string baseValueSource = null;
DependencyPropertyDescriptor depProp =
DependencyPropertyDescriptor.FromProperty(prop);
if (depProp != null)
{
baseValueSource = DependencyPropertyHelper.GetValueSource(
propertySource, depProp.DependencyProperty).BaseValueSource.ToString();
resolvedProp = depProp;
}
string name = resolvedProp.Name;
object value = resolvedProp.GetValue(propertySource);
base.Add(new WpfElementProperty(name, value, baseValueSource));
}
}
A far more challenging thing to implement was the logic which lazily loads the property information and snapshot image of each element in the visual tree. Initially, I serialized all of that information for every element in the tree in the ElementTreeVisualizerObjectSource
's GetData
method, seen above. It turns out that Visual Studio has a timeout monitor for that method (deadlock detection?), which caused the method to be terminated before it was able to complete serializing the data for large visual trees. When the timeout occurs, the Woodstock UI does not appear, and the visualizer is unusable.
I worked around this problem by redesigning the way that the property information and the snapshot image are created for each WpfElement
instance. Instead of creating them all in one fell swoop, I create them on an as-needed basis. This prevents all of that data from being serialized at once, which makes the GetData
method lightning fast (so it won't timeout).
When the WpfElementTree
is created in the ElementTreeVisualizerObjectSource.GetData
method, the WpfElement
instances do not have any of that extra information. When the user selects an element in Woodstock's TreeView
, it checks to see if the newly selected WpfElement
is already populated with the property information and snapshot data. If not, we send that element over to the debuggee-side code and expect it to be returned with the missing data. That code is in the ElementTreeVisualizerForm
class, as seen below:
void treeView_AfterSelect(object sender, TreeViewEventArgs e)
{
WpfElement element = (WpfElement)e.Node.Tag;
if (!element.IsPopulated)
{
WpfElement populatedElement = this.CreatePopulatedElement(element);
e.Node.Tag = populatedElement;
if (e.Node.Parent != null)
{
WpfElement parentElement = (WpfElement)e.Node.Parent.Tag;
parentElement.ReplaceChild(element, populatedElement);
}
element = populatedElement;
}
this.RefreshGrid(element);
this.RefreshImage(element);
}
The method which actually requests that the ElementTreeVisualizerObjectSource
populates the WpfElement
is seen here:
WpfElement CreatePopulatedElement(WpfElement element)
{
List<WpfElement> children = new List<WpfElement>(element.Children);
element.Children.Clear();
MemoryStream inputStream = new MemoryStream();
_binaryFormatter.Serialize(inputStream, element);
Stream outputStream = _objectProvider.TransferData(inputStream);
inputStream.Close();
WpfElement populatedElement =
(WpfElement)_binaryFormatter.Deserialize(outputStream);
populatedElement.Children.AddRange(children);
_elementSnapshots.Add(populatedElement.Snapshot);
return populatedElement;
}
When the object provider's TransferData
method is invoked, it results in the ElementTreeVisualizerObjectSource
's TransferData
method to be called back in the debuggee process. That logic is seen below:
public override void TransferData(
object target, Stream incomingData, Stream outgoingData)
{
WpfElement element =(WpfElement)_binaryFormatter.Deserialize(incomingData);
Debug.Assert(!element.IsPopulated, "Element should not be populated.");
WpfElement populatedElement = this.CreatePopulatedElement(element);
_binaryFormatter.Serialize(outgoingData, populatedElement);
}
The method which populates the WpfElement
with property information and a snapshot is seen here:
WpfElement CreatePopulatedElement(WpfElement element)
{
DependencyObject depObj = _elementTree.GetDependencyObjectByID(element.ID);
WpfElementPropertyList properties = new WpfElementPropertyList(depObj);
Bitmap snapshot = VisualSnapshot.TakeSnapshot(depObj);
element.Populate(properties, snapshot);
return element;
}
One crucial thing to notice here is that we have an association between the real WPF element and a WpfElement
instance which represents it. That link is formed by a simple integer ID value. WpfElement
has an ID
property, and the real WPF element is put into a Dictionary<int, DependencyObject>
, where the integer key is the ID value.
The two ID values are set up in the WpfElement
constructor:
private WpfElement(
DependencyObject currentElem,
DependencyObject initialElem,
Dictionary<int, DependencyObject> elementMap)
{
_id = WpfElement.elementCount++;
elementMap.Add(_id, currentElem);
}
When the debugger process sends over a WpfElement
to be populated, the element's ID is used to look up the real WPF object in the dictionary.
The image processing code can be found in the VisualSnapshot
class. Its primary method looks like this:
public static Bitmap TakeSnapshot(object target)
{
Bitmap bitmap = null;
if (target is BitmapSource)
{
bitmap = BitmapSourceToGdiImage(target as BitmapSource);
}
else if (target is FrameworkElement)
{
FrameworkElement fe = target as FrameworkElement;
if (0 < fe.ActualWidth && 0 < fe.ActualHeight)
{
BitmapSource bitmapSource = CaptureVisual(fe);
bitmap = BitmapSourceToGdiImage(bitmapSource);
}
}
if (bitmap == null)
{
return new Bitmap(VisualSnapshot.UnavailableSnapshot);
}
else
{
using (bitmap)
return new Bitmap(bitmap);
}
}
- November 12, 2007 – Created the article.
- November 13, 2007 - Added the "Visual Studio Version Information" section, and provided a VS2008 version of the Woodstock assembly.
- November 13, 2007 - Improved performance by changing
WpfElementProperty
to a struct instead of a class. Added logic which ensures the display order of columns in the DataGridView
. Added a number to every non-leaf node in the TreeView
indicating how many descendants that element has. Updated all of the file downloads.
- November 14, 2007 - Improved the way that an element's properties are discovered, thanks to a tip from Andrew Smith at Infragistics. Now that code is faster, and all attached properties will be included as well. Added the "Limitation" section. Updated all of the file downloads.
- November 15, 2007 - After receiving tons of great feedback and encouragement, I added several features: the property filtering
TextBox
, the CheckBox
which allows you to hide the attached properties in the grid, and a column of links which copy a Google search to your clipboard. Also, I made some optimizations to the serialization code, hoping to avoid the dreaded "Function evaluation timed out" error message. I updated all of the file downloads.
- November 15, 2007 - Fixed the way that Google search URLs are created for attached properties. Updated the file downloads.
- November 15, 2007 - Thanks to Karl Shifflett, I discovered a bug which only exists on Vista where exceptions are thrown as soon as Woodstock was opened. To fix the problem, I changed the "Property Name" column to use buttons instead of links. That solved the weird remoting/GDI+ issue. I updated the file downloads.
- November 15, 2007 - Wow, a lot of updates today! I added in Florian Kruesch's code and added him as an author of this article. Adding in his code pointed out a bug in my logic where
DependencyObject
s that are not in a visual tree were being handled incorrectly. That has been fixed. I updated the file downloads.
- November 16, 2007 - I implemented a great suggestion from Karl Shifflett, so that the Woodstock window appears immediately and shows a "splash screen" while the visual tree information is created on a worker thread. This really improves the responsiveness of the UI, making Woodstock more usable. I also cleaned up the code a bit and added more comments. I updated the file downloads.
- November 17, 2007 - I made the element information lazy-loaded to vastly improve performance and avoid timing out. I added the "Lazily Loading Element Information" subsection. Doing this also enabled me to provide a snapshot of every element in the tree, not just the one the user initially selected. Also, I fixed a few bugs and gave the snapshot image some scrollbars so that you can view the entire image without having to resize the window (thanks to Rick Eberle for the suggestion). I updated all the file downloads.
- November 18, 2007 - I got rid of the attached ID property for elements in the visual tree, and instead use a dictionary to associate a
WpfElement
with a DependencyObject
. Thanks to Karl Shifflett for pointing out that simpler and more efficient way of creating the object associations. I updated the file downloads and modified the "Lazily Loading Element Information" subsection.
- November 18, 2007 - I cleaned up the code a bit and updated the relevant snippets in the article. Added the "Making Woodstock Work with XBAPs" section. Updated the file downloads.
- November 19, 2007 - I implemented performance optimizations after performing stress tests. Now the nodes in the
TreeView
are loaded on-demand, and the children of a WpfElement
are temporarily removed when it is being sent over to the debuggee process to be populated. I updated the file downloads.
- November 20, 2007 - Karl Shifflett fixed the visual snapshot code so that the elements are not clipped. I then took his code and simplified it a little bit and put it into Woodstock. Due to his amazing efforts in this project, I decided to add him as an author of this article! I updated the file downloads.
- November 20, 2007 - Dropped a new build of Woodstock which was compiled in the RTM of VS2008. Also, I removed the images of Woodstock which I got from the Peanuts web site, because some weenies were whining about it, and one guy even threatened to report me to whoever officially owns those images (geez, talk about a buzz-kill...). All the files were updated.
- November 21, 2007 - I fixed a bug in the snapshot image logic, and updated the file downloads.
- November 23, 2007 - I put in a bug fix which Karl Shifflett pointed out in the
VisualCapture
class. Elements whose ActualWidth
or ActualHeight
is between 0.0 and 1.0 were causing an exception to be thrown because the corresponding dimension of the Bitmap
for that element has an invalid value of 0. I updated the file downloads.