Click here to Skip to main content
Email Password   helpLost your password?

Mole v4 For Visual Studio - With Editing Released

All versions of Mole have been replaced by Mole v4 For Visual Studio.

Please refer to this article for the latest information and code: Mole v4 For Visual Studio - With Editing

This article is here for informational purposes. The downloads have been removed. Please download and use Mole v4 For Visual Studio - With Editing. Thank you!


Authoring

No question about it, Mole was a team effort.

Karl created Mole and wrote this article. Josh Smith and Karl developed Woodstock and Mole during the same time frame and were able to draw from each others efforts. Woodstock acted as a prototype, or proof-of-concept, upon which the design and some algorithms in Mole are based. Mole adds a rich array of user-friendly, useful, and refined additions to the core functionality chiseled out in Woodstock.

I really appreciate working together with Josh Smith on critical sections of code. We have gone back and forth with our projects trying to deliver tools that developers will utilize.

Introduction

Mole is a multifunction Visual Studio Visualizer which allows you to inspect elements in the WPF visual tree, as well as all properties of those elements..

The focus of this article is to introduce Mole, explain how to use it, and to introduce techniques for creating and debugging visualizers.

Mole's Feature Set

Background

There are a growing number of 3rd party development tools for WPF developers. In addition there have been very good articles recently posted on CodeProject presenting visualizers for WPF. When a great friend and coauthor Josh Smith wrote Woodstock for WPF a few weeks ago I got pretty excited about how Visual Studio Visualizers that target WPF can really assist developers. I started making a list of features I wanted and began working on Mole.

Mole was my first visualizer and first real WinForms program I've done with .Net 2.0. I spent most of my time with WPF, ASP.NET, SQL Server and Windows Services. So starting from scratch would prove to be challenging at times.

Mole grew alongside Woodstock as it was being developed and refined. Josh and I worked hard to develop fast performing visualizers. We both tried some crazy things during this process of refinement, some of these I wouldn't post on a billboard.

To give you a comparison of what we were up against from a data loading perspective, consider the Text Visualizer which ships with Visual Studio. It allows you to view long strings in a multiline TextBox. Text Visualizer opens quickly and displays the string.

Mole displays an entire visual tree and over a hundred properties in a DataGridView for any object selected in the TreeView! Let's face it, the visualizer needs to load fast or it won't get used. What you'll see in this article is a highly refined visualizer which runs lightning fast. When running Mole in stress tests with absurdly large visual trees (over 10,000 elements) it was able to open in less than a second. Bearing in mind that most sane developers would never create visual trees of that size, it is safe to say that Mole is fast enough to be usable by the most impatient developers out there.

Where did the name Mole come from? The original name was Lucy, another member of the Peanuts gang. After a possible legal problem with using cartoon images in our visualizers was brought to light, I decided to change the name and colors and came up with Mole and the earth colors. Moles love to dig. This Mole, burrows around your visual tree and returns requested information.

Being a fan of Business Intelligence applications and Drill Through or Drill Back, I wanted to give my visualizer some Drill Through capabilities. That is where the idea to be able to view property data that is stored in collections came from. Ask yourself, how many times have you been using the Visual Studio debugger and come upon a property that has its values stored in a collection and you can't see the values without having to do some more typing or supply an indexer to see the data. Not with Mole. If the property stores its data in a collection, just click on the collection icon and all the values in the collection will be displayed. In testing I've viewed trigger collections, row and column collections and items collections that are custom classes just to name a few. You can also see collection data in the run-time XAML viewer.

If you are debugging and perform a Quick Watch on a ComboBox, 99% of the time you want to view the SelectedValue, SelectedIndex, Selected... properties, right? Those other 130 properties I really never look at. So I added in the ability to select "favorite properties" by Framework Type. That way you can view just the properties for that Type that you usually want to view. With the click of the mouse you can toggle showing just favorites or all properties. These favorites are persisted so that you'll have them the next time the visualizer opens.

Installation

There are a number of options available to you for getting Mole installed on your machine. You can just install Mole and being Molenating, or you can download the source, build and install your build.

Mole has been tested on x64 and x32 systems, Vista and XP.

Standalone Install

Source Code Install

I have provided source for both VS2005 and VS2008. The only difference is the reference to Microsoft.VisualStudio.DebuggerVisualizers.DLL. For VS2005 the file version is 8.0.0.0 and for VS2008 the file version is 9.0.0.0.

Visual Studio Visualizers and XBAPs

One big advantage Mole and Woodstock have over some other tools, is that you can use them to debug XBAP applications. Here is what you need to do to enable that:

  1. Right-click on the XBAP project in Solution Explorer and open the Properties page.
  2. Under the Security tab select the "This is a full trust application" RadioButton.
  3. Be sure to set it back later on to partial trust in order to properly test deployment.

Thanks to Josh Smith and Code Project member "ivolved" for this tip!

Using Mole

Mole is opened just like any other Visual Studio Visualizer, you click on the small magnifying glass icon, this displays the visualizers lists, then you select the visualizer you want to use. Mole is programmed to show up in the visualizers list when your mouse is over an object that is of type DependencyObject. This snapshot was taken from the code window. You can also open visualizers from the Watch windows.

When Mole opens it looks like this. There is a 600px image width restriction here at Code Project. I reduced this image from the default width of 1024 to 600. I will cover each element in detail with full size images. When you resize Mole, the window persists its size so on your next Mole session the window will be the same size as you left it.

Screenshot - mole2.gif

Element Tree - Mole's Garden

The Mole's Garden contains a TreeView which displays the current visual tree of the WPF application being debugged. TreeView initially selects the object you selected in Visual Studio. The TreeNode text is formatted as follows:

Element Type - Element Name (Count Of Descendants)

Some nodes were not assigned names and some nodes do not have any child nodes associated with them. Notice the ListBox named lbTest has 1,111 items in it. Mole's Garden has a minimum width set so that the buttons beneath it will always be visible.

Elements are displayed in one of four colors. White is an unselected element. Light Gray is the selected element when the TreeView has lost focus. Blue is the selected element when the TreeView has focus. Moccasin is the initially selected object you moused over in Visual Studio.

The Light Brown 4 pixel line separating the two TabControls is a GridSplitter. You may size the two regions. The size of the two panels is persisted when the size is changed so that the panels will be displayed the same when Mole is reopened.

Select Initial button will select the object that you initially selected in Visual Studio.

Expand All button will expand all TreeNodes in the entire TreeView.

Collapse All button will collapse all TreeNodes in the entire TreeView

Expand Down button will expand all TreeNodes that are children of the currently selected TreeNode.

Collapse Down button will collapse all TreeNodes that are children of the currently selected TreeNode.

Selected Element Tab Header

The selected element TabControl has four TabPages.

The label below the Element Properties TabPage heading is formatted as follows.

[Object Type] [Has Favorites] [Object Name] [location loaded from]

Two of these need some explanation.

[Has Favorites] text will appear if the selected element Framework Type has favorites associated with it. This is useful to remind you that this Type has favorites associated with it.

[location loaded from] informs you whether the data was fetched from the process being debugged or read from the local cache. Data from the Properties tab and Visual tab is lazy loaded. The data is only loaded when the element is selected in the TreeView and the tab page for that type of data is visible. The Visual Studio Visualizer architecture allows for two way conversations to take place across process boundaries, by using .NET Remoting. Mole takes full advantage of this technology and only requests the data the users wants to display. This lazy loading design drastically reduces the time it takes for Mole to load up.

Element Properties Grid

The Element Properties grid displays each of the selected item's properties. Columns with underlined header text can be sorted by clicking on the column header. Like many features in Mole, the selected column sort is persisted between Mole debugging sessions. It is very easy to add more columns to this grid, so any suggestions from the community for more data to view are welcome.

When the help icon is clicked, it opens Google.com using your default browser and sets the query string to search for the property name and selected item Type.

The Favorites column (Fav) indicates if this property is one of your favorites for the selected Type. You may select or deselect favorites by clicking the CheckBox. See the information below for a full explanation of the favorites feature.

The Category Name column is the name of the category assigned to it on the property declaration with the Sysem.ComponentModel.Category attribute. To see an example of this attribute look at the MoleTabControl.vb file in the solution source.

Value Source identifies the source for the property value. Only dependency properties can have a value source. If you are not familiar with the idea of value sources, please read up on the topic at MSDN BaseValueSource Enumeration.

The rightmost column, Is DP, identifies whether this property is a dependency property or not.

If a property stores its values in a collection, and the collection actually contains items, the collection icon will display in the Coll column. When this icon is clicked, a modal window will open with a grid containing all the values of all the items in the collection. The below image displays the collection from the Grid's Children property.

Screenshot - molecollections.gif

Search

Mole allows you to search and filter the properties displayed. Mole searches are logical AND searches. If you have more than one search condition applied then the property must meet both conditions in order to be displayed.

The "Select Search Location" ComboBox allows you to select where and how you want the search text to be used. The search is conducted as you type in your search text.

The "Show All" Button clears any searching/filtering you have performed so that you can view all of the properties.

The "Show Attached Properties" CheckBox toggles the display of properties that are attached properties.

The "If Has Favorites, Only Show Favorites" CheckBox toggles the "favorite properties" display behavior. If checked and the selected element's type has one or more favorites assigned, then only properties assigned as favorites will be displayed. The beauty of this technique is that it permits you to leave this CheckBox checked all the time and when you select a type which has favorites, the displayed properties will only be those that you want. If the selected element's type has no favorite properties associated with it, then you will see all of that element's properties. After using Mole for a day, you'll have your favorites all established.

The "If Has Collections, Only Show Collections" CheckBox works the same as the above Checkbox, except its logic is applied to properties with collections.

Molenator's Blog link will take you to my blog. I will establish a special section on my blog for Mole and future visualizers.

Element Visual

The "Element Visual" TabPage displays an image of the selected element. The above image is from my RoutedEvent visual trace program. The image is lazy loaded and the label under the Elements Visual header indicates where the image was loaded from, cache or fetched.

The displayed image can easily be copied to the Windows Clipboard by clicking on the "Copy Image" Button.

This tab is pretty cool because it gives you insight into how the various elements in the visual tree participate in the rendering process. You can select this tab to display it, then click on an item in the element tree and press the arrow key and go up or down the TreeView's nodes to see what each element looks like. The "Copy Image" Button makes it super easy to get snapshot images and copy them to other documents, emails, etc.

Element XAML

Expanded View

Screenshot - molexamlexpanded.gif

Compressed View

Screenshot - molexamlcompressed.gif

The "Element XAML" TabPage displays the run-time XAML which was transformed into HTML and loaded in the WebBrowser control The label under the WebBrowser control follows the format of the other two TabPages.

The 'Font +' and 'Font -' Buttons allow you to change the font size of the displayed text. This setting is also persisted between Mole sessions.

The two RadioButtons allow you to select the format for the displayed XAML, expanded or compressed. This setting is also persisted between Mole sessions.

You may right-click the WebBrowser control to access the copy, print and view source functions.

This TabPage took longer to write than any other section of code. I really wish Microsoft would expose the default XSLT object which the WebBrowser control uses internally or just add a public method like, RenderXML and the control would just perform the transformation like it does when it opens a file. This would make this control SO MUCH easier to use. At any rate, I downloaded a defaultss.xslt and edited it to get the above transformation. This file is in the solution source and is also an embedded resource.

This TabPage is cool because if the element contains collections, those collections will be displayed here. I have loaded 20,000 items into a ListBox and viewed the XAML for them in this XAML viewer.

Favorites

The Favorites TabPage presents a TreeView of your favorite properties, grouped by Framework Type.

Limitations

So far, the only limitation I've run into is the Element XAML. Under certain circumstances the System.Windows.Markup.XamlWriter.Save function can throw a StackOverFlow Exception or a GenericTypeSerialization Exception. This program has no control over this. The StackOverFlow will be reported in a MessageBox and the visualizer will close. Just reopen it and don't select that element and view its XAML.

These two exceptions are thrown on the other side of the remoting conversation that handles data transfer requests. Once its stack is corrupted, that process must be terminated, thus ending our visualizer session.

You would get these same exceptions in your own code if you called System.Windows.Markup.XamlWriter.Save and passed this same dependency object.

Mole & Visualizers 101

Before going any further please get some background on visualizers. Once you have the basics, the rest is just plain fun!

The first article I read was Creating Debugger Visualizers with Visual Studio 2005 by Julia Lerman. She does an outstanding job of getting you started. Another good starting point is the MSDN Visualizer Architecture article. Also there many articles here on CodeProject and the Internet covering visualizers.

There are four partners, running in two processes that are working together in the visualizer world.

Debugger Side
Debuggee Side

Visual Studio plays the middle man handling communications between the two processes. The bottom line is, your visualizer UI has no data, until it makes a request to the visualizer data object. All data which moves between the two processes must be serialized. This is important to remember when building your data structures.

Both sides of the conversation are active throughout the visualizer's life. This allows your UI to make requests to the data object whenever it needs more data to display. The communications plumbing Microsoft provided is incredibly fast, even with all the serialization and deserialization going on.

Visualizer Project

If you are used to writing executables, this is going to blow your mind. Your visualizer complies into one class library .dll, part of which runs in one process and the other part is running in the other process. The classes from the Debugger process communicate through Visual Studio to classes in the Debuggee process.

How Does Mole's Project Fit Into This?

Visual Studio is only concerned about two classes in your visualizer the debugger and debuggee.

First your Debugger Side class which derives from DialogDebuggerVisualizer. In Mole, this is the Mole.Burrow class.

Second your Debuggee Side class which derives from VisualizerObjectSource. In Mole, this is the Mole.MoleVisualizerObjectSource class.

Visual Studio identifies these classes by looking at the following attribute you have placed on the assembly in which your visualizer lives. Since all visualizers must be in certain directories, it is very easy for Visual Studio to locate all visualizers on your system.

<Assembly: System.Diagnostics.DebuggerVisualizer(GetType(Mole.Burrow), _
  GetType(Mole.MoleVisualizerObjectSource), _
  Target:=GetType(System.Windows.DependencyObject), _
  Description:="Mole WPF Visualizer (Burrow back into your visual tree.)")>

First attribute parameter is the type of our visualizer (Mole.Burrow).

Second is the type of our data source (Mole.MoleVusualizerObjectSource).

Third tells Visual Studio what type our visualizer wants to visualize. (in Mole speak that is, "what type Mole wants to Burrow into and molenate"). In our case, Mole can visualize DependencyObjects, or any class which descends from DependencyObject.

The fourth attribute parameter is the description Visual Studio displays in the listing of available visualizers when you are debugging. Glance back to the first image in this article and you'll see the above description.

Mole.Burrow - Debugger Side - UI Side
Imports Microsoft.VisualStudio.DebuggerVisualizers
Public Class Burrow
    Inherits DialogDebuggerVisualizer

    Protected Overrides Sub Show(ByVal windowService As _
    IDialogVisualizerService, _
    ByVal objectProvider As IVisualizerObjectProvider)

        'rather than following the normal pattern of calling GetData or 





        'GetObject at this point, we will defer these calls to frmMole





        'by passing the objectProvider to the form





        '





        Using frm As New frmMole(objectProvider)
            frm.StartPosition = Windows.Forms.FormStartPosition.CenterScreen
            windowService.ShowDialog(frm)
        End Using
    End Sub

End Class

Here is how the communication really works. Notice the second parameter, objectProvider. The objectProvider is the object the debugger side will use when communicating with the data object. Just make your API calls using the objectProvider. Read about the IVisualizerObjectProvider Interface.

In most visualizer examples on the web, you'll see calls being made to the Data Object in this class with the results being passed to a Form or message box. Mole needs to constantly communicate with the debuggee-side data object so we just passed the objectProvider to the Form and allowed the form to take over the responsibility of data communication.

So you can see, Visual Studio does not open your form, you do. Visual Studio only makes a method call to Sub Show. The rest is up to you.

Mole.MoleVisualizerObjectSource - Debuggee Side - Data Object Side
Code abbreviated here for clarity
Public Class MoleVisualizerObjectSource
    Inherits VisualizerObjectSource

    ''' <summary />





    ''' Returns an abstract view of the entire visual tree of the application





    '''being debugged.





    ''' This sub is actually called by Visual Studio when the frmMole 





    '''makes the 





    ''' objectProvider.GetObject call to the IVisualizerObjectProvider





    ''' </summary />





    Public Overrides Sub GetData(ByVal target As Object, _
      ByVal outgoingData As System.IO.Stream)

        MoleVisualizerObjectSource.Serialize(outgoingData, BuildTree(target))

    End Sub

End Class

When your visualizer UI first opens, it requests data by calling the GetObject method on the objectProvider. This in turn, calls the GetData method seen above.

Your Visualizer Object Source class is passed a reference to the object that was selected in the Visual Studio debugger. This class is working with and has full access to the selected object. This allows Mole to access the visual tree of the application being debugged.

One more item I need to make perfectly clear. Visual Studio only creates one instance of your Visualizer Object Source and makes its calls to this same object. This is allows you to have module level objects that will survive between your calls to the Visualizer Object Source. Mole takes full advantage of this as you will see in a moment.

MoleVisualizerObjectSource.Serialize is a Shared helper function that wraps the BinaryFormatter.Serialize method. This method along with the BinaryFormatter.Deserialize method are used by both the debuggee and debugger sides.

The BuildTree function does all the work building the outgoing data structure that represents your visual tree.

Mole Visualizer Form - Debugger Side - UI Side - Requesting Data
Private Sub LoadItemsOnUIThread()
    _objTree = CType(_objectProvider.GetObject(), Tree)

    CompleteLoading()

End Sub

Private Sub BackgroundWorker1_DoWork( _
  ByVal sender As System.Object, _
  ByVal e As System.ComponentModel.DoWorkEventArgs) _
  Handles BackgroundWorker1.DoWork

    ' Get the BackgroundWorker object that raised this event.





    Dim worker As System.ComponentModel.BackgroundWorker = _
      CType(sender, System.ComponentModel.BackgroundWorker)
  
    'Assign the result of the computation to the Result property 





    'of the DoWorkEventArgs object.  





    'This is will be available to the RunWorkerCompleted eventhandler.





    e.Result = _objectProvider.GetObject()

End Sub

Private Sub BackgroundWorker1_RunWorkerCompleted( _
  ByVal sender As Object, _
  ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
  Handles BackgroundWorker1.RunWorkerCompleted

    If e.Error IsNot Nothing Then
        MessageBox.Show("Bummer.  Exception while loading data." & _
          vbCrLf & vbCrLf & e.Error.ToString, "Problems...", _
          MessageBoxButtons.OK, MessageBoxIcon.Stop)
          
        Me.Close()
        Exit Sub
    End If

    _objTree = CType(e.Result, Tree)

    CompleteLoading()

End Sub

Now things can get a little confusing. You'll notice that the above code fragments both make the same call to _objectProvider.GetObject(). In the lifetime of the visualizer, only one of these methods will get called. Which one depends on how you are using the visualizer.

If you are just simply using the visualizer the background worker code will be called.

Please Read This Note: If you want to debug Mole (meaning you want to step through Mole's code while it's visualizing an object), you can't spawn background worker threads because Visual Studio will throw an exception. Because of this limitation and the fact that I wanted to be able to debug Mole, I had to provide two ways, sync and async for Mole to get its data from the Data Object.

Synchronous Data Object Call

The LoadItemsOnUIThread sub calls _objectProvider.GetObject on the form's UI thread and get the data. Again, this type of call is required when debugging a visualizer.

Asynchronous Data Object Call

The .NET Framework provides a rich set of multithreading tools for developers. The BackgroundWorker object simplifies multithreading by exposing a very simple interface to developers that encapsulates the complexities of multithreading. Visual Studio allows you to drop a BackgroundWorker right on to your Form, you could also create the BackgroundWorker in code. So how you code your multithreading is up to you.

The call to _objectProvider.GetObject is made asynchronously. When it completes the BackgroundWorker1.RunWorkerCompleted sub casts the data.

The CompleteLoading sub processes the populated _objTree object, filling the Element Tree and Element Properties grid.

How To Debug A Visualizer
Public Class MoleVisualizerObjectSource
    Inherits VisualizerObjectSource

    Public Overrides Sub GetData(ByVal target As Object, _
      ByVal outgoingData As System.IO.Stream)
        MoleVisualizerObjectSource.Serialize(outgoingData, BuildTree(target))
    End Sub
    
    ''' <summary />





    ''' This function makes debugging a Visualizer a snap.  





    ''' 1.  Set desired breakpoints inside your Visualizer





    ''' 2.  Call this method from another project





    ''' </summary />





    ''' Note, that other project will need to reference 





    '''   Microsoft.VisualStudio.DebuggerVisualizers





    ''' 





    Public Shared Sub TestMoleVisualizer(ByVal obj As DependencyObject)

        Dim vdh As VisualizerDevelopmentHost = _
          New VisualizerDevelopmentHost(obj, GetType(Mole.Burrow), _
          GetType(Mole.MoleVisualizerObjectSource))

        vdh.ShowVisualizer()

    End Sub    

End Class    

After looking at this architecture, you might be wondering, how am I going to debug my visualizer? Microsoft really did an outstanding job making this task simple by providing the VisualizerDevelopmentHost object.

In your VisualizerObjectSource derived class, add a Shared method that takes the same type of object that your visualizer does. In Mole's case, it's DependencyObject.

Take notice of the VisualizerDevelopmentHost constructor. It looks very similar to the attribute I described before which decorates the class library.

Calling TestMoleVisualizer From Another Application
'you must disable all multithreading inside the visualizer when using 





'this method






Mole.MoleVisualizerObjectSource.TestMoleVisualizer(Me.btnViewVisualTree)

To debug your visualizer you'll need to do a few simple things. First, in the project which has the object your want to visualize, you must add a reference to Microsoft.VisualStudio.DebuggerVisualizers.

Next place the above line of code where you want the visualizer to start. This call replaces you putting a breakpoint in your code, right clicking on the object and selecting the visualizer you want to use. The parameter needs to be the same Type object your visualizer supports.

Next set breakpoints on one or both sides of your visualizer, debugger and debuggee.

Now run the project and when the breakpoints are reached, Visual Studio will break and allow you to debug your visualizer.

Mole Concepts

Data Classes

With all the new visualizer concepts introduced above, I wanted to simplify the design of Mole. Since Mole's data objects are used on both the debugger and debuggee sides, I decided early on to not attach code to the data objects. You'll find a similar pattern being used in WCF with respect to data contracts. All of the data processing logic is separate from the data itself, which makes the entire system easier to understand and maintain. It can be confusing when some methods on an object can only be called when running in the debugger or debuggee process.

The debuggee side creates data. The debugger side consumes the data.

Mole's three data classes are in the Tree.vb, TreeElement.vb and TreeElementProperty.vb files.

Tree Class

The Tree class is used to contain the entire visual tree you are visualizing. When the class is first created, most of the data about individual elements in the tree is not loaded. This radically speeds up returning the visual tree from the data source. The lazy loading feature of Mole will load those unpopulated objects when the user requests to view them for the first time.

One bridge we had to cross was how to get the additional information that the debugger side object needed when the user wants to inspect an element in the tree. We use a generic Dictionary on the debuggee side to hold a reference to every object in the visual tree and use a unique integer as the key. That key is assigned to the Id property in the TreeElement class. The below code fragment shows how the dependency object reference is saved in the Dictionary. When the debugger side needs to request information from the debuggee side, it passes the key along with the request.

Private Function BuildElement(ByVal root As DependencyObject, _
   ByVal objFirstVisual As DependencyObject, _
   ByRef intInitialElementId As Integer) As TreeElement
   
    'this is the value used to uniquely identify each element





    'allows both sides debugger and debuggee to refer to the same





    'object across process boundaries





    _intElementIdCounter += 1
    
    'save dependency property for future calls to TransferData





    _dictVisualTree.Add(_intElementIdCounter, root)
    
    ...
    ...
End Function

TreeElement Class

The TreeElement class holds data displayed in the Element Tree TreeView control on the left side of the UI. This object tracks the state of lazy loaded objects XAML, Image and Properties.

TreeElementProperty Class

The TreeElementProperty class holds the data which is loaded into the Element Property DataGridView control. This data is lazy loaded. If you want to extend Mole to display more information in the DataGridView, this is the class to modify.

Element Tree TreeView Control

The TreeView control is metaphorically speaking the 175lbs brick slowing Mole down. It is the loading of the TreeView nodes that takes most of the time, especially if you have thousands of visual tree elements. Special thanks to Josh for his code to lazy load the TreeView's nodes. His version was better than mine, so Mole uses his.

Not only are the properties, images and XAML lazy loaded, but the TreeView control nodes are lazy loaded. This is what gives Mole the performance it requires.

Cast Reduction

Another performance enhancement came from making a custom MoleTreeNode class that derives from TreeNode. This class exposes a TreeElement property to store a reference to the above TreeElement class that is associated with a node in the Element Tree TreeView. Using the TreeNode.Tag property is a viable alternative to subclassing TreeNode but then you have to cast the Tag property object every time you want to access the data.

Drilling Back For Collection Data

One of Mole's cool features is its ability to execute an on the fly drill through operation to retrieve a properties values that are stored in a collection. I'll show the code for this feature here.

'function takes an object and returns a sorted list of all 





'public instance property names.





'





Private Function GetColumns(ByVal obj As Object) As List(Of String)
    Dim objList As New List(Of String)
    For Each mi As MemberInfo In obj.GetType.GetMembers( _
       BindingFlags.Public Or BindingFlags.Instance)
       
        If mi.MemberType = MemberTypes.Property Then
            objList.Add(mi.Name)
        End If
    Next
    objList.Sort()
    Return objList
End Function

'function takes a dependency object, a property name and returns a DataTable





'with all property values for each object in the collection.





'





Private Function MakeDataTableFromIEnumerable(ByVal target As _
   DependencyObject, ByVal strPropertyName As String) As DataTable

    Dim prop As PropertyInfo = target.GetType.GetProperty(strPropertyName)
    Dim obj As IEnumerable = CType(prop.GetValue(target, Nothing), _
        IEnumerable)
    Dim dt As New DataTable
    Dim bolColumnsCreated As Boolean = False
    Dim intX As Integer

    'get a little more performance





    dt.RemotingFormat = SerializationFormat.Binary
    
    Try

        For Each objItem As Object In obj

            'this code block only runs once to get the column names





            If Not bolColumnsCreated Then
                bolColumnsCreated = True
                
                'this calls the above GetColums function 





                For Each s As String In GetColumns(objItem)
                    dt.Columns.Add(s)
                Next

            End If

            If dt.Columns.Count = 0 Then
                Return dt
            End If

            intX += 1

            Dim dr As DataRow = dt.NewRow

            For Each c As DataColumn In dt.Columns

                Dim propInfo As PropertyInfo = _
                    objItem.GetType.GetProperty(c.ColumnName)

                If propInfo IsNot Nothing Then

                    Dim value As Object = _
                        propInfo.GetValue(objItem, Nothing)

                    If value IsNot Nothing Then
                        dr.Item(c.ColumnName) = value.ToString

                    Else
                        dr.Item(c.ColumnName) = "null"
                    End If

                End If

            Next

            dt.Rows.Add(dr)

            If intX > _
               INTEGER_MAXIMUM_ROWS_RETURNED_BY_COLLECTION_DATA_TABLES Then
                Exit For
            End If

        Next

    Catch ex As Exception
        'during debugging you can place a breakpoint here to stop and 





        'troubleshoot





        Debug.WriteLine(ex.ToString)
    End Try

    dt.AcceptChanges()
    Return dt

End Function

The above code is simple reflection in action. The function will iterate though the target collection items. After reading the first item, iterate through the property names and add a table column for each property. Now for the every item in the collection, iterate through the column names and retrieve the property's value and add it to the DataTableRow.

Finally it returns the DataTable for rendering in the UI. Note the DataTable's RemotingFormat property has been set to SerializationFormat.Binary. This is a performance enhancement from .NET 2.0.

Moles Home Page

For everything Mole, visit Mole's home page by clicking here.

Mole Speak (fun stuff)

Sample questions you can ask fellow WPF developers:

So do you Molenate? :P

Close

We hope this article can help someone learn a little more about Visual Studio Visualizers For WPF. We also hope that Mole helps WPF developers across the world have an easier time debugging complex user interfaces.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionVS2010 Supported?
JimboM
9:01 20 Jul '09  
I've tried installing Mole on VS2010 beta 1, but the install was not quite so similar to VS2008, and thus it doesn't work.

I'm looking forward to having all that Molenating goodness in VS2010...
AnswerRe: VS2010 Supported?
Karl Shifflett
17:52 20 Jul '09  
JimboM,

Sorry, not supported you. But, I'll have a new version of Mole for Visual Studio 2010 Beta2.

Cheers, Karl

» CodeProject 2008 MVP, CodeProject 2009 MVP

My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


GeneralRe: VS2010 Supported?
JimboM
7:46 7 Nov '09  
Hi Karl,

Curious if you had an updated timeframe for Mole on VS2010/.NET 4.0 yet?

--- Jim ---
GeneralRe: VS2010 Supported?
Karl Shifflett
7:52 7 Nov '09  
Jim,

I have two other projects ahead of Mole.

Mole 2010 will be a big project. Want to move Mole's UI to WPF and will need to add support for many new things.

I'm thinking this will become my life in Jan 2010.

Cheers, Karl

» CodeProject 2008 MVP, CodeProject 2009 MVP

My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


GeneralTimeout
Bug Me Not
5:31 18 Feb '09  
This utility seems great, but I keep getting a timeout trying to evaluate an element in a window (trying to view Element Xaml). Text (retyped from dialog)

Bummer. System.Exception: Function evaluation timed out.
Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.PrivateCallback.MaybeDeserializeAndThrowException
....
Mole.frmMole.LoadXAML

Once you get the first timeout, you cannot proceed with any more visualization, and get:

System.Exception: Function evaluation disabled because a previous function evaluation timed out. You must continue execution to reenable function evaluation.
at Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.PrivateCallback.MaybeDeserializeAndThrowException(Byte[] data)
at Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.ManagedShim.DelegatedHost.CreateViewer(IntPtr hwnd, HostServicesHelper hsh, SafeProxyWrapper proxy)

Is there any way to increase this timeout and recompile this code? Thanks.
GeneralRe: Timeout
Bug Me Not
6:16 18 Feb '09  
Here is the complete initial stacktrace. This is in VS2008, I should've mentioned.

---------------------------
Exception
---------------------------
Bummer. System.Exception: Function evaluation timed out.

at Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.PrivateCallback.MaybeDeserializeAndThrowException(Byte[] data)

at Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.PrivateCallback.WriteDataToStreamAndThrowOnException(Byte[] data, Stream dataStream)

at Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.PrivateCallback.WriteDataToStreamAndThrowOnException(Byte[] data)

at Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.PrivateCallback.Microsoft.VisualStudio.DebuggerVisualizers.IVisualizerObjectProvider.TransferData(Stream outgoingData)

at Mole.frmMole.LoadXAML(MoleTreeNode objTreeNode)
---------------------------
OK
---------------------------
GeneralRe: Timeout
Karl Shifflett
6:59 15 Mar '09  
We have seen this before with a very limited set of XAML. The reason for the exception is that the XAML didn't parse correctly. We have no control over this piece of the puzzle.

Sorry you had the issue.

Cheers, Karl

» CodeProject 2008 MVP, CodeProject 2009 MVP

My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


GeneralI am getting a Mole.TransferDataRequest InvalidCastException
Paul Schwartzberg
6:55 9 Dec '08  
I am getting a Mole.TransferDataRequest exception...

This is occuring between the Mole.Visualizer.dll's in:
(a) C:\Documents and Settings\MyUserId\My Documents\Visual Studio Codename Orcas\Visualizers
and
(b) D:\Dokuments\Visual Studio Codename Orcas\Projects\mole\Mole For Visual Studio\Mole.WPF.TestBench\bin

Something is being transfered by the Mole.Visualizer.dll's in both locations and boom an exception...

What to do?

Yes, the dll's are identical.

Regards,
Paul

Here's the exception text:
*********** Exception Text **************
Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.RemoteObjectSourceException: [A]Mole.TransferDataRequest can not in [B]Mole.TransferDataRequest be converted. The type "A" is from "Mole.Visualizer, Version=4.2.0.0, Culture=neutral, PublicKeyToken=null" im Kontext "Default" at the location "D:\Dokuments\Visual Studio Codename Orcas\Projects\mole\Mole For Visual Studio\Mole.WPF.TestBench\bin\Mole.Visualizer.dll".. The type be "B" is from location "Mole.Visualizer, Version=4.2.0.0, Culture=neutral, PublicKeyToken=null" im the context "LoadFrom" at the location "C:\Documents and Settings\UserId\My Documents\Visual Studio Codename Orcas\Visualizers\Mole.Visualizer.dll".. ---> System.InvalidCastException:

Rgrds,
Paul

AnswerSolution Re: I am getting a Mole.TransferDataRequest InvalidCastException
Paul Schwartzberg
7:18 9 Dec '08  
The solution is to delete the dll at this location:
D:\Dokuments\Visual Studio Codename Orcas\Projects\mole\Mole For Visual Studio\Mole.WPF.TestBench\bin

regards,
paul

Rgrds,
Paul

GeneralRe: Solution Re: I am getting a Mole.TransferDataRequest InvalidCastException
Josh Smith
7:40 9 Dec '08  
I'm glad you got it working. Thanks for sharing the solution.

:josh:
Try Crack![^]
Sleep is overrated.

GeneralGreat
Paul Conrad
8:36 29 Dec '07  

...is all I can pretty much say Smile


"Real programmers just throw a bunch of 1s and 0s at the computer to see what sticks" - Pete O'Hanlon

GeneralRe: Great
Karl Shifflett
13:25 30 Dec '07  
Paul,

Thank you. We appreciate your encouragement!

Cheers, Karl

Just a grain of sand on the worlds beaches.

GeneralFeature Requests
DrewS
6:10 28 Nov '07  
I've been using the visualizer today and its really great. I have a couple of feature requests if you get the chance (and assuming you agree these are worthwhile Smile):

* It would be nice to be able to associate a Favorite with a base type. For example, I want to make a favorite for the ActualWidth so that its a favorite for any derived element with that property (its defined on FrameworkElement).

* It would be nice to see the LogicalTree. Barring that, or perhaps in addition, you could show the LogicalParent and TemplatedParent as links on the element properties tab or have options in a right click menu for items in the tree to be able to navigate to them.

* It would be nice to have forward/back buttons to navigate the history of the items you selected in the tree. Reflector does this and I find it very useful when navigating around the tree.

* This isn't trivial but it would be nice to be able to modify the value of a property in the Element Properties list.

* It would be nice to have the snoop like feature of being able to delve into the properties of an object. E.g. If you want to see the values of a Template set on an element. Currently if value of a property is a complex object, you can't see the values of that object.

BTW, this is minor but you get an exception dialog if you click on the Help or Coll headers.

Thanks,
Andrew
GeneralRe: Feature Requests
Josh Smith
6:18 28 Nov '07  
Those are some great ideas. We'll definitely discuss/investigate them. Thanks.

:josh:
My WPF Blog[^]
Without a strive for perfection I would be terribly bored.

GeneralRe: Feature Requests
Karl Shifflett
6:36 28 Nov '07  
Drew,

You are the Man! Thank you so much for your awesome suggestions. I'm on it.

Josh, lets try and speak this evening.

Cheers, Karl

Just a grain of sand on the worlds beaches.

GeneralRe: Feature Requests [modified]
Karl Shifflett
9:18 28 Nov '07  
Drew,

1. Is no problem. I can set up global favorites and keep the typed favorites or would you rather like it to just be global.

If we implement this, then the checkbox at the bottom behavior will need to change, since it now shows you everything if the type does not have any assigned favorites. Or we could add a "Show Type & Global Only" would solve this problem.

2. I'll speak with Josh about this tonight. Don't see a problem though.

3. No problem

4. For simple values types this is easy. I'll have to remember to invalidate the Image cache after sending a property back so that the image can be rerendered. Maybe the changes posted are not immediately recomplied and available? This will be interesting to see how this works!

5. I've look at this and for many complex objects this is real simple, like looking into the DataContext, very cool suggestion. I'll play around with this and give it my best shot. I think moving the "Col" column next to the "Value" column will be benificial. Renaming it to (+) would be a good idea too, or put a little drill icon in the grid for drilling back. Promise not to play drill.wav or dig.wav otherwise Josh will kill me. This way, for each of the values the user can click at what they are drilling or digging (Mole) into. Great suggestion.

6. I'm an idiot for missing this. When I added the "Col" column I must have missed visiting the Header click code. That was done at 3:00am last Sunday morning. 18 hours straight face down coding, like the good old days!


-- modified at 14:42 Wednesday 28th November, 2007

Cheers, Karl

Just a grain of sand on the worlds beaches.

GeneralRe: Feature Requests
Josh Smith
9:31 28 Nov '07  
Karl Shifflett wrote:
1. Is no problem. I can set up global favorites and keep the typed favorites or would you rather like it to just be global.

If we implement this, then the checkbox at the bottom behavior will need to change, since it now shows you everything if the type does not have any assigned favorites. Or we could add a "Show Type & Global Only" would solve this problem.


I think that just having a "global" list of property names is not only sufficient, but better. If a property name is in the list but the selected element does not have such a property, we just ignore it. That will be much more simple, and enables the feature request Drew made. If we do that, we can change the Favorites tab so that it provides a list of all the current favorite property names, and a way to add/remove property names. What do you say?

:josh:
My WPF Blog[^]
Without a strive for perfection I would be terribly bored.

AnswerRe: Feature Requests
Karl Shifflett
9:41 28 Nov '07  
Where would we get the list of properties from on the Favorites Tab?

We could easily allow the removal but adding? (Well, we could always reflect the .NET Framework and currently running solution to get all the possible property names on 4 - 5 threads and build a list that would be pretty long, just kidding.)

Cheers, Karl

Just a grain of sand on the worlds beaches.

GeneralRe: Feature Requests
Josh Smith
9:44 28 Nov '07  
Karl Shifflett wrote:
Where would we get the list of properties from on the Favorites Tab?


Let the user type a property name into a TextBox. If they misspell the property name, we just ignore it. Nice and simple! Wink

:josh:
My WPF Blog[^]
Without a strive for perfection I would be terribly bored.

GeneralRe: Feature Requests
Karl Shifflett
9:56 28 Nov '07  
AHH bummer, I wanted them to scroll through hundreds of properties!

Cheers, Karl

Just a grain of sand on the worlds beaches.

GeneralRe: Feature Requests
Josh Smith
9:59 28 Nov '07  
Karl Shifflett wrote:
AHH bummer, I wanted them to scroll through hundreds of properties!


Laugh

:josh:
My WPF Blog[^]
Without a strive for perfection I would be terribly bored.

NewsRe: Feature Requests
Karl Shifflett
18:07 28 Nov '07  
Drew,

We are 80% done with your suggestions.

Mole is SCREAMING FAST.Cool Visibly faster than the current release with the serialization and other optimizations you suggested. I have sub second load times with average WPF applications on my system. I learned a lot from your pointers!

I found a few other places to shave off some more CPU cycles too.

Josh is working hard on the Logical Tree suggestion.

I need to finish the drill through into objects tomorrow.

Thanks again for all your suggestions, you are, The Man!

Cheers, Karl

Just a grain of sand on the worlds beaches.

AnswerRe: Feature Requests
Karl Shifflett
3:11 29 Nov '07  
Drew,

Did a lot of testing try to be able to change the values of any element in the tree, but

objectProvider.ReplaceData()
objectProvider.ReplaceObject()

Only work with the initially selcted object. So we can allow for only that object to be edited within Mole.

I did try altering values of visual tree elements from with the Object Source, no exceptions were thrown but the value changes were not persisted.

Cheers, Karl

Just a grain of sand on the worlds beaches.

GeneralRe: Feature Requests
DrewS
4:31 1 Dec '07  
That's interesting I wonder why that would matter. I could understand that if you changed something visual (e.g. Width) that it would not update the rendering of the element because of the retained graphics nature of WPF but that might be addressable by calling UpdateLayout on the element from within the ReplaceData callback. Could you explain a little more about the behavior you saw when you changed the value in lets say the ReplaceData callback? I did a quick and dirty test of doing a setvalue on the element within the replacedata callback and getvalue returned that value. It also seemed to retain that value after the visualizer was closed. Note, I tested in VS 2005 - not sure if there is different behavior in VS 2008.

Thanks,
Andrew
GeneralRe: Feature Requests
Karl Shifflett
4:44 1 Dec '07  
Andrew,

I can adjust an element using the ReplaceData method, no problem. This method only works with the target object (initially selection object in Visual Studio).

What I was trying, was changing another object within the Visual Tree that was not the target. This is what I could not get to persist.

Were you able to change an object that was not the target?

Progress: We hope to post Mole II later today. Mole II is WAY faster than Mole, new UI too. Moletones are gone, back to the default colors.

Josh and I really dug in deep and discovered optimizations and refactorings.

I'm working on the Moloscope which allows multi-depth drilling into objects, collections, custom classes, etc. Supports history feature. Go back with right click, Backspace or left arrow.

If you want I can send you the code when its done while we write the article.



Cheers, Karl

Just a grain of sand on the worlds beaches.


Last Updated 31 Dec 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010