Visual Studio 2005
Visual Studio 2008
General
Introduction
ADCollectionsVisualizer is a Visualizer add-in for Visual Studio 2005 and Visual Studio 2008 which allows a wide range of collection and dictionary-style objects to be easily examined.
Background
One of the fantastic new features of the Visual Studio 2005/2008 IDE is the ability to use Visualizers. These are utility windows that attach themselves to various object types within the Visual Studio environment. When you hover the mouse over a variable of one of the supported object types, a magnifying glass icon appears within the popup tooltip, and clicking it opens the Visualizer window for that particular variable, allowing the variable content to be viewed much more easily.
Several Visualizers are included with Visual Studio by default, to allow text to be viewed (as plain text, HTML or XML) and to allow DataTables to be examined more closely. The IDE allows further Visualizers to be written, however, such as ADCollectionsVisualizer.
ADCollectionsVisualizer allows a whole variety of collection and dictionary classes (and objects for all classes that inherit from these classes) to be viewed in a grid, and also copied to the clipboard and saved to disk if required.
Using the Code
The visualizer can be installed by either running the installation executable, or by unzipping the DLL and placing it into your My Documents\Visual Studio 2005\Visualizers or My Documents\Visual Studio 2008\Visualizers directory, as appropriate.
Note that if you are compiling the visualizer from its source code, you can use a Build Event to automatically copy the visualizer to the installation directory. You can find this by double-clicking the My Project icon, then selecting the Compile tab and clicking the Build Events button (such a build event is configured within the provided project file — you will most likely need to change the path to wherever your My Documents directory is located).
After this has been done, simply hover the mouse over a collection object within the IDE whilst in break mode. A small magnifying glass icon should appear; click on this to open the visualizer.
The visualizer supports a wide range of collection-type objects, including:
System.Collections
classes
System.Collections.ArrayList
System.Collections.BitArray
System.Collections.HashTable
System.Collections.Queue
System.Collections.SortedList
System.Collections.Stack
System.Collections.Specialized
classes
System.Collections.Specialized.HybridDictionary
System.Collections.Specialized.ListDictionary
System.Collections.Specialized.NameValueCollection
System.Collections.Specialized.OrderedDictionary
System.Collections.Specialized.StringCollection
System.Collections.Specialized.StringDictionary
- All classes derived from
System.Collections.CollectionBase
- All classes derived from
System.Collections.Specialized.NameObjectCollectionBase
System.Collections.Generic
classes
System.Collections.Generic.Dictionary
System.Collections.Generic.List
System.Collections.Generic.LinkedList
System.Collections.Generic.Queue
System.Collections.Generic.SortedDictionary
System.Collections.Generic.SortedList
System.Collections.Generic.Stack
- IIS classes, as used by
System.Web.HttpRequest.Cookies
System.Web.HttpRequest.Files
System.Web.HttpRequest.Form
System.Web.HttpRequest.Headers
System.Web.HttpRequest.Params
System.Web.HttpRequest.QueryString
System.Web.HttpRequest.ServerVariables
System.Web.HttpResponse.Cookies
- VB6-compatible collections
Microsoft.VisualBasic.Collection
(values only — keys cannot be retrieved from this type of collection)
The code itself consists of three main classes:
CollectionVisualizer
ObjectSource
PreviewForm
CollectionVisualizer
contains the visualizer itself, along with all of the DebuggerVisualizer
assembly attributes. There are quite a lot of these, to handle all of the different types of collection and dictionary classes that the visualizer supports:
<Assembly: DebuggerVisualizer(GetType(CollectionVisualizer),
GetType(ObjectSource), Target:=GetType(Collections.ArrayList),
Description:="Collection Visualizer")>
<Assembly: DebuggerVisualizer(GetType(CollectionVisualizer),
GetType(ObjectSource), Target:=GetType(Collections.BitArray),
Description:="Collection Visualizer")>
<Assembly: DebuggerVisualizer(GetType(CollectionVisualizer),
GetType(ObjectSource), Target:=GetType(Collections.Hashtable),
Description:="Collection Visualizer")>
[...etc...]
Notice that two of the project classes are referenced within every one of the attributes: the first is the CollectionVisualizer
class, which is responsible for doing the work, and the second is the ObjectSource
which packages up all of the data into a form which can be serialized in order for the visualizer to access it. Further details on this are below. The third class reference within each of the attributes points to the type of class for which the visualizer is to function. This changes for each attribute, and the repeated attributes allow all of the supported classes to be handled by the same visualizer class.
If you wish to know more about how to write visualizers, you will find other articles within The Code Project that explain this in great detail.
The latest release now also supports Generic collection objects. These are specified in a slightly different manner within the assembly attributes:
<Assembly: DebuggerVisualizer(GetType(CollectionVisualizer),
GetType(ObjectSource),
Target:=GetType(collections.Generic.Dictionary(Of ,)),
Description:="Collection Visualizer")>
<Assembly: DebuggerVisualizer(GetType(CollectionVisualizer),
GetType(ObjectSource), Target:=GetType(collections.Generic.List(Of )),
Description:="Collection Visualizer")>
[...etc...]
The generic class type is specified using the class name followed by the Of
keyword in parentheses, but with no class type specified. Where multiple class types are required, a comma is used to separate the types but once again no types are specified. This allows the visualizer to work with the generic classes regardless of the types of data that are being stored within them.
The visualizer uses a custom VisualizerObjectSource
in order to package up the data to be visualized. The IDE serializes the data that is to be visualized, and some of the collection types supported by ADCollectionsVisualizer are not able to be serialized. To work around this, the ObjectSource
class extracts all of the data from the collection and puts it into an ADO.NET DataTable
, representing each object's value as a simple String
. This DataTable
is then serialized and passed to the visualizer. The ObjectSource
class is responsible for recognising all of the different collection types that are supported and decoding their data into the DataTable
. For the most part this is straightforward, but some of the types (in particular objects that derive from NameObjectCollectionBase
) are a bit more tricky to access than they might have been.
Finally the PreviewForm
displays the data to be visualized on the screen. It uses a DataGrid
for its presentation, and also offers the ability to copy the collection data to the clipboard or save it to disk in CSV, text or XML formats.
Points of Interest
There are a couple of areas of code that might prove useful or interesting to anyone that hasn't encountered them before — they certainly had me guessing for a while.
The first is the code that accesses the values within an object derived from NameObjectCollectionBase
. I could see that there were two protected members within the class called BaseGetAllKeys
and BaseGetAllValues
— obviously these would be exactly what I need to extract all the data from the collection. However, as they are protected, my code doesn't have any direct access to them.
I used a rather hacky piece of reflection to gain access to these functions. I definitely would not recommend using techniques such as this in production code, but in the context of what the visualizer is trying to do I think they are an acceptable technique. Without them, I would be unable to support collections based upon this class.
The second thing that took some experimentation and research to get right is the customisation of the DataGrid
. By default the grid allows the values to be edited (which is not something that the visualizer supports at present) and highlights the selected cell with a gray background. I created a class named CustomGridTextBoxColumn
which derives from .NET's DataGridTextBoxColumn
and added some code to prevent editing and to properly control the foreground and background colours of the cells. Despite being a bit of a pain to set up, it works very well and provides exactly the functionality I was looking for.
The code that implements the DataGridTextBoxColumn
is as follows:
Public Class CustomGridTextBoxColumn
Inherits DataGridTextBoxColumn
Protected Overrides Sub Edit(ByVal source As CurrencyManager,
ByVal rowNum As Integer, ByVal bounds As System.Drawing.Rectangle,
ByVal [readOnly] As Boolean, ByVal displayText As String,
ByVal cellIsVisible As Boolean)
Return
End Sub
Protected Overrides Sub Paint(ByVal g As System.Drawing.Graphics,
ByVal bounds As System.Drawing.Rectangle,
ByVal source As System.Windows.Forms.CurrencyManager,
ByVal rowNum As Integer, ByVal backBrush As System.Drawing.Brush,
ByVal foreBrush As System.Drawing.Brush,
ByVal alignToRight As Boolean)
Try
backBrush = New SolidBrush(SystemColors.Window)
Dim o As Object = Me.GetColumnValueAtRow(source, rowNum)
If o Is DBNull.Value Then
foreBrush = New SolidBrush(SystemColors.GrayText)
Else
foreBrush = New SolidBrush(SystemColors.ControlText)
End If
MyBase.Paint(g, bounds, source, rowNum, backBrush, foreBrush,
alignToRight)
Finally
If backBrush IsNot Nothing Then backBrush.Dispose()
If foreBrush IsNot Nothing Then foreBrush.Dispose()
End Try
End Sub
End Class
As you can see, two of the class procedures are overridden. The Edit
procedure exits immediately, without calling into the base class. This prevents the edit from taking place. The Paint
procedure does call into the base class (and that's still where the actual drawing of the text takes place), but prior to doing it replaces the foreground and background brushes that would have been used, and substitutes its own to ensure that it has full control over the colours.
If you wish to learn more about changing and extending the behaviour of the DataGrid
, you can find a huge amount of information in the DataGrid section of George Shepherd's Windows Forms FAQ.
History
Version 1.2 (2008-09-10)
- Added support for
System.Collections.Generic
classes
- Visual Studio 2008 support added
Version 1.1 (2007-09-14)
- Added support for collections deriving from
Collections.CollectionBase
- Released source-code
Version 1.0 (2007-06-03)