A meaningful way to visualize variables in the debugger
The Autos, Locals and the four watch windows provided by the Visual Studio IDE allow you to inspect and modify variable values. However, for viewing complex objects having many properties and fields, this is not the friendliest interface to inspect values. The main issue is the lack of an overall picture. For example, for a
DataTable, you may want to visualize the data in the table without inspecting the
Rows collection and the
DataRow objects it contains. This detailed view of variables, though useful, is not always the perfect visual for debugging.
Fortunately, debuggers in Visual Studio 2005 and Visual Studio 2008 have a feature called Debugger Visualizer which allows an alternative and friendly way to view complex objects. A good example of this is the DataSet Visualizer which shows the actual data of the
DataTable in a grid and also allows you to modify it. It provides a high level view of the
DataTable which is sometimes useful to quickly figure out bugs or issues. Visualizers can be easily written for custom types, and if you are a framework author, you can supply visualizers for types in your framework.
While working with WPF, I found it a little painful that there was no easy way to visualize the UI hierarchy while debugging. Then, I found a neat debugger visualizer that visualized the WPF visual tree, by David Sleeckx. After using that for some time, it struck to me that it will be neat to have a XAML visualizer which will allow visualizing the XAML representation of an object and also allowing modifying the object by modifying the XAML. I personally have used the visualizer extensively to modify templates in a debugging session. This allows me to test things without cancelling the debugging session or starting a new build. In the next section, I will take you on a guided tour of the visualizer and we will see it in action.
Using the XamlVisualizer - A guided tour
If you have not already downloaded and installed the visualizer, go ahead and do so as per the instructions in the Download and Installation section. Assuming you have followed the steps in the Download and Installation section correctly, you are now ready to use the visualizer. Let's start with a quick debugging session. I have used VS 2008 to develop this step by step tutorial, but the instructions are equally valid if you have VS 2005 and the Visual Studio Tools for Orcas installed.
First, let's start off by creating a new WPF project. For the sake of this tutorial, we will create a new WPF application in C#. Let's name the project XamlVisualizerTest as shown in the screenshot below. Also, notice the combo box in the upper right corner whose selected value is ".NET Framework 3.0". This is the new multi-targeting feature of VS 2008. When you select .NET Framework 3.0, you indicate that you will like to use the WPF/WCF/WF technologies which are available in .NET Framework 3.0 but you don't want to use the .NET Framework 3.5 features such as LINQ. What VS 2008 does in such a case is that it does not add references to LINQ assemblies. But you are free to use C# 3.0 features such as anonymous classes, lambda expressions, and auto implemented properties - these features are not specific to LINQ or .NET Framework 3.5.
As a courtesy, the wizard creates Window1.xaml, Window1.xaml.cs, App.xaml, and app.xaml.cs (though in real world apps, I have to always delete the Window1.xaml file, so it is more of a pain than a courtesy). Open the
Window1.xaml.cs file and put a breakpoint at the end of the constructor. To immediately see the effect of the debugger, run the debugging session (press F5). The break point will be hit in the constructor as shown below:
Notice the magnifying glass icon on the extreme right in the Value column for the variable
this. Clicking on it will lead to the XAML Visualizer being shown:
Notice that the XAML code is not the same as the XAML you specified in the Window1.xaml file. This is because the XAML shown by the XAML Visualizer is the run-time representation of the object. So if a property specified in XAML gets modified programmatically, then the XAML visualizer will show the programmatically modified value. Also, notice that the element name is
Window1 instead of
Window as we specified in the Window1.xaml. Again, the reason is that the XAML used in Window1.xaml is used to define the
Window1 class, whereas the XAML shown by the XAML Visualizer is the representation of any object with the type
Window1. Finally, notice that the Replace button is disabled as it is not possible to modify the
Viewing more complex XAML content
A common task when working in WPF is to use control templates. Even if you don't specify a control template, a control inherits a template automatically from the theme. The default control templates for controls used by WPF are available for all the themes supported by WPF with the Windows SDK samples. But in this section, we will see how we can get the markup of a template using the XAML Visualizer.
To view the control template, we need to have more interesting content in our window. So, let's modify Window1.xaml to include a
Button, as shown below:
Title="Window1" Height="300" Width="300">
<RowDefinition Height="40" />
<Button Grid.Row="1" HorizontalAlignment="Right" Margin="0,9,9,11"
Let's launch the debugger again, and when the debugger hits the breakpoint in
InitializeComponent, type the expression
button1.Template in the watch window, as shown in the screenshot below.
As you can see in screenshot, the XAML for the default template in all its details is shown in the XAML Visualizer window. Also, notice that the Replace button is active, which gives us something to do in the next section.
Replacing the XAML content
One nice feature of modern debuggers is that they allow the values of variables to be modified during the debugging process. The same ability is also available in the XAML Visualizer. You can modify the XAML code in the visualizer window and see the effects in the application being debugged. To try it out, let's modify the XAML content of
button1's template as shown below:
The change is highlighted in the above screenshot. We change the
Blue, and the result, unsurprisingly, appears as shown below:
We managed to change the color to blue without modifying the main application code. It's all between the debugger and debuggee, the main code still remains unmodified. If you stop the debugger and re-run the application, then the button's appearance will revert back.
Replacing the XAML is powerful but...
Let me start this section by saying that XAML replacement will not always work. In this section, we will understand why and when it will not work. Let's run our sample app again in the debugger, and break in the
InitializeComponent function. This time, let's visualize the
button1 variable as shown below and replace the contents as shown in the highlighted portion of the screenshot below:
You will expect that the button text will change once you click the Replace button. But alas, this does not happen. You will see that the original text remains. Is this a bug? Although this behavior is not expected on the outset, if we give it a more careful thought, we will realize that the visualizer did exactly what it was supposed to do. It replaced the
button1 member variable with the XAML supplied in the visualizer dialog. The
button1 member variable held a reference to the button in the WPF visual tree, and now it refers to the new object created by the XAML visualizer. Replacing the member variable does not mean that the content is replaced in the visual tree. In fact, if you visualize the
button1 variable again, you will see that it has the new XAML we supplied. This is an important thing you need to be aware of when using the XAML visualizer.
How Visualizers work?
In this section, I will quickly go through the internal working of the XAML Visualizer. XAML Visualizer has three main classes as shown below:
DialogDebuggerVisualizer and the
VisualizerObjectSource belong to the VS IDE. The
DialogDebuggerVisualizer is the most important class, and a visualizer should have a class derived from
DialogDebuggerVisualizer has a method called
Show which is invoked by the IDE when the user wants to display a value using the visualizer. It is here that the visualizer displays the dialog, gets any changes by the user, and asks Visual Studio to replace the object if the user intends to do so.
protected override void Show(IDialogVisualizerService windowService,
Stream dataStream = objectProvider.GetData();
using (XamlVisualizerForm displayForm = new XamlVisualizerForm())
displayForm.XamlText = new StreamReader(dataStream).ReadToEnd();
displayForm.IsObjectReplaceable = objectProvider.IsObjectReplaceable;
if (windowService.ShowDialog(displayForm) == DialogResult.OK)
MemoryStream replacementDataStream = new MemoryStream();
using (XmlWriter xmlWriter =
objectProvider parameter passed to the
Show method is used to obtain the actual object to display in the Visualizer. In our case, this is the streamed XAML. Data is transferred back and forth between the debuggee process and the debugger process using streams. In our case, this stream consists of the XAML representation. At this point, you might be wondering how the stream got the XAML in the first place? This is where the
XamlVisualizerObjectSource comes into picture. The
XamlVisualizerObjectSource has methods that run both in the debuggee process and the debugger process. The method which runs in the debuggee process is called
GetData, and it is responsible for writing the contents of the object being visualized to a stream. In the case of the XAML Visualizer, we directly use the
XamlWriter class to write the contents to the stream.
It is this stream which is obtained by the
objectProvider.GetData call in the
Show method discussed above. The other method of interest is
CreateReplacemntObject, which takes a stream passed from the debugger and converts it to an object which will replace the object in the debugger.
It is no surprise that we are using
XamlReader to load the data and create the replacement object. So now, we have a
VisualizerObjectSource, and an object of a given type being visualized. How do we link all three? This is done through an assembly attribute named
DebuggerVisualizerAttribute, as shown below:
//Apply XamlVisualizer Visualizer to a WPF Visual
Target=typeof(Visual), Description = "XAML Visualizer")]
//Apply XamlVisualizer Visualizer to a WPF Style
Target = typeof(Style), Description = "XAML Visualizer")]
//Apply XamlVisualizer Visualizer to templates
Target = typeof(FrameworkTemplate), Description = "XAML Visualizer")]
In the above code, we indicate that we want to use the XAML Visualizer to visualize WPF Visuals, Styles, and Templates. If you want to add more types (for example, a
FlowDocument), you can do so by adding an attribute as follows:
//Apply XamlVisualizer Visualizer to documents
Target = typeof(FlowDocument), Description = "XAML Visualizer")]
You can add this attribute to all
objects or all DependencyObjects if you want to, but that is not recommended as XamlWriter and XamlReader may not always work.
Installing the Visualizer
To install the Visualizer:
- Download the binary for the appropriate version from the download link on the top of this article.
- Extract the zip and copy the XamlVisualizer.dll to My Documents\Visual Studio 2005\Visualizers for VS2005, or to My Documents\Visual Studio 2008\Visualizers for VS2008.
I started writing this article about two months back, though I have been using the visualizer for a long time. By the time the article got posted, Josh Smith had posted Woodstock for WPF, which is similar to the WPF tree visualizer by David Sleeckx. Both XAML Visualizer and Woodstock can be used together.