In this blog post I am going to explain how you can customize the complete view of the debugging window during debugging of application. By complete view means, where you can add own Properties, can customize the result, manipulate the data, hide properties which may not need during debugging etc. In one of my previous blog post, I have explained how we can customize the view of the debugging windows using DebuggerBrowseable and DebuggerDisplay attributes over Few Tips on Customizing Debugging Window View in Visual Studio . But, both these two attributes are limited to customize the view for a specific class and they can only show you the information for the particular class members . In this blog post you will see how we can create new view for some existing debugging view using “DebuggerTypeProxy” attributes. From the below snaps you can understand what kind of customization we can do inside a debugging window.
To start with this, first let’s see what is the default behavior of debugging window for the below block of code. I took one simple example of Student Data, where I have 3 classes “Student” , “Address” and “Marks” .
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | class Student { /// <summary> /// Gets or sets the roll. /// </summary> /// <value>The roll.</value> public int Roll { get ; set ; } /// <summary> /// Gets or sets the name. /// </summary> /// <value>The name.</value> public string Name { get ; set ; } /// <summary> /// Gets or sets the marks. /// </summary> /// <value>The marks.</value> public Marks Marks { get ; set ; } /// <summary> /// Gets or sets the addresses. /// </summary> /// <value>The addresses.</value> public Address Addresses { get ; set ; } } /// <summary> /// Address of Students /// </summary> class Address { /// <summary> /// Gets or sets the address1. /// </summary> /// <value>The address1.</value> public string City { get ; set ; } /// <summary> /// Gets or sets the address2. /// </summary> /// <value>The address2.</value> public int Pin { get ; set ; } } /// <summary> /// Marks of Student for different subjects /// </summary> public class Marks { /// <summary> /// Gets or sets the subject1. /// </summary> /// <value>The subject1.</value> public int Subject1 { get ; set ; } /// <summary> /// Gets or sets the subject2. /// </summary> /// <value>The subject2.</value> public int Subject2 { get ; set ; } /// <summary> /// Gets or sets the subject3. /// </summary> /// <value>The subject3.</value> public int Subject3 { get ; set ; } } |
Where Student class having two properties of type Address and Marks classs. Now, below is your main methods where we are creating some dummy mock data for student. Our main objective is to view the Debug window.
01 02 03 04 05 06 07 08 09 10 | static void Main( string [] args) { List<Student> student = new List<Student>(); student.Add( new Student { Roll = 1, Name = "Abhijit" , Marks= new Marks {Subject1=56,Subject2=66,Subject3=67} , Addresses = new Address { City = "add1" , Pin = 312312 } }); student.Add( new Student { Roll = 2, Name = "Abhishek" , Marks = new Marks {Subject1=78,Subject2=66,Subject3=67}, Addresses = new Address { City = "add3" , Pin = 123123 } }); student.Add( new Student { Roll = 3, Name = "Rahul" , Marks = new Marks {Subject1=78,Subject2=43,Subject3=77}, Addresses = new Address { City = "add5" , Pin = 123123 } }); student.Add( new Student { Roll = 4, Name = "Sunil" , Marks = new Marks {Subject1=74,Subject2=96,Subject3=57}, Addresses = new Address { City = "add11" , Pin = 57567 } }); student.Add( new Student { Roll = 5, Name = "Atul" , Marks = new Marks {Subject1=78,Subject2=76,Subject3=47}, Addresses = new Address { City = "add12" , Pin = 57567 } }); student.Add( new Student { Roll = 6, Name = "Kunal" , Marks = new Marks {Subject1=56,Subject2=66,Subject3=67}, Addresses = new Address { City = "add12" , Pin = 46456 } }); } |
Now, just put a breakpoint at the end of the main method and watch the debug data tip. It will look likes below,
Where you can see the all Address, Marks are in expanded mode. Even if you look at the Watch window, you have to expand each and every list of object to get the data.
Though this is a very simple class structure where you have only few set of data, but in real life there are many cases where you have to deal with complex objects. All of those object properties may not relevant for you to check or you may want to very simple view of your debugging window, where you can see the on demand data.
In this scenarios, You can use the “DebuggerTypeProxy” attributes along with “DebuggerBrowseable”. Let me first show you how this things works, then I will explain the details of the use. To create a customize debugging view, create a proxy class, by proxy it means, it will bypass the actual debugging windows and will the the proxy window for the class you defined the proxy.
So, create a internal class for the Student Proxy shown as below.
This call is going to the view for student class debug mode. So create all the custom properties over here. Skip those properties which you don’t want to see, change the properties names which is more meaning full during debugging, Add new properties which by which you can get some manipulated data. Below snaps showing few of them.
See, from the above image you can see I have used some existing properties such as “Roll”, “Name” and “City” . I have also made changes on properties name “Pin” to “PinNo” along with that I have added two new properties which is used to show all the marks of students in a single view and New properties rank to calculate the rank based on the marks.
Till now what I have discussed is all about the creating the proxy class, which is nothing but your new view for debugging window for the students objects.
Now, you have to assign this proxy class to let your existing class know that, debugger will use the proxy view during debugging.
You can see in the above image, I have assigned StudentDebuggerProxy Class as DebuggerTypeProxy attributes. You can also see, I have also specified “DebuggerDisplay” attributes to get the exact name of the student in debugging datatip.
That’s all. Again run the program with the same breakpoint position and check for the debugging view data tip.
Yes, this is exactly a new view of your debugging window. This view is showing only those data that are defined your proxy class. You can see all of your different classes data with out expanding. In a single properties you can see the value of different properties. You can also see the impact of using “DebuggerDisplay” attributes, as it’s showing like “Details information of “Student Name””. You can check the same in Quick watch window also,
Yes, quick watch windows is containing only those information that are defined on debugger proxy class. Here you can see all the data with in a single view without expanding. Below snaps showing the difference between two quick watch window.
I hope, I have explained well to understand to customizing the debugging windows view. Below is the complete code snippet for this sample application. You can run and test the steps that I have mentioned.
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | namespace DebugWindowCustomizationDemo { class Program { static void Main( string [] args) { List<Student> student = new List<Student>(); student.Add( new Student { Roll = 1, Name = "Abhijit" , Marks= new Marks {Subject1=56,Subject2=66,Subject3=67} , Addresses = new Address { City = "add1" , Pin = 312312 } }); student.Add( new Student { Roll = 2, Name = "Abhishek" , Marks = new Marks {Subject1=78,Subject2=66,Subject3=67}, Addresses = new Address { City = "add3" , Pin = 123123 } }); student.Add( new Student { Roll = 3, Name = "Rahul" , Marks = new Marks {Subject1=78,Subject2=43,Subject3=77}, Addresses = new Address { City = "add5" , Pin = 123123 } }); student.Add( new Student { Roll = 4, Name = "Sunil" , Marks = new Marks {Subject1=74,Subject2=96,Subject3=57}, Addresses = new Address { City = "add11" , Pin = 57567 } }); student.Add( new Student { Roll = 5, Name = "Atul" , Marks = new Marks {Subject1=78,Subject2=76,Subject3=47}, Addresses = new Address { City = "add12" , Pin = 57567 } }); student.Add( new Student { Roll = 6, Name = "Kunal" , Marks = new Marks {Subject1=56,Subject2=66,Subject3=67}, Addresses = new Address { City = "add12" , Pin = 46456 } }); } } /// <summary> /// Student Debugger Proxy Class /// </summary> /// internal class StudentDebuggerProxy { private Student student; /// <summary> /// Initializes a new instance of the <see cref="StudentDebuggerProxy"/> class. /// </summary> /// <param name="student">The student.</param> public StudentDebuggerProxy(Student student) { this .student = student; } /// <summary> /// Gets the roll. /// </summary> /// <value>The roll.</value> public int Roll { get { return this .student.Roll; } } /// <summary> /// Gets the name. /// </summary> /// <value>The name.</value> public string Name { get { return this .student.Name; } } /// <summary> /// Gets the city. /// </summary> /// <value>The city.</value> public string City { get { return this .student.Addresses.City; } } /// <summary> /// Gets the pin. /// </summary> /// <value>The pin.</value> public int PinNo { get { return this .student.Addresses.Pin; } } /// <summary> /// Gets the marks details. /// </summary> /// <value>The marks details.</value> public string MarksDetails { get { string marksDetails = string .Format( "First Subject - {0} Second Subject - {1} Third Subject - {2} | Total = {3} " , this .student.Marks.Subject1.ToString(), this .student.Marks.Subject2.ToString(), this .student.Marks.Subject3.ToString(), ( this .student.Marks.Subject1 + this .student.Marks.Subject2 + this .student.Marks.Subject3).ToString()); return marksDetails; } } /// <summary> /// Gets the rank. /// </summary> /// <value>The rank.</value> private string Rank { get { int total = this .student.Marks.Subject1 + this .student.Marks.Subject2 + this .student.Marks.Subject3; string grade; if (total > 200) grade = "A" ; else if (total > 150) grade = "B" ; else grade = "C" ; return grade; } } } [DebuggerDisplay( "Details Information of {Name}" )] [DebuggerTypeProxy( typeof (StudentDebuggerProxy))] class Student { /// <summary> /// Gets or sets the roll. /// </summary> /// <value>The roll.</value> public int Roll { get ; set ; } /// <summary> /// Gets or sets the name. /// </summary> /// <value>The name.</value> public string Name { get ; set ; } /// <summary> /// Gets or sets the marks. /// </summary> /// <value>The marks.</value> public Marks Marks { get ; set ; } /// <summary> /// Gets or sets the addresses. /// </summary> /// <value>The addresses.</value> public Address Addresses { get ; set ; } } /// <summary> /// Address of Students /// </summary> class Address { /// <summary> /// Gets or sets the address1. /// </summary> /// <value>The address1.</value> public string City { get ; set ; } /// <summary> /// Gets or sets the address2. /// </summary> /// <value>The address2.</value> public int Pin { get ; set ; } } /// <summary> /// Marks of Student for different subjects /// </summary> public class Marks { /// <summary> /// Gets or sets the subject1. /// </summary> /// <value>The subject1.</value> public int Subject1 { get ; set ; } /// <summary> /// Gets or sets the subject2. /// </summary> /// <value>The subject2.</value> public int Subject2 { get ; set ; } /// <summary> /// Gets or sets the subject3. /// </summary> /// <value>The subject3.</value> public int Subject3 { get ; set ; } } } |
Summary : Use the DebuggerTypeProxy attribute attribute when you need to significantly change the debugging view of a type. You can customize the properties, add new properties, you can also manipulate the result to show. This can be used at the assembly level. No private members of the debugger proxy class won’t be visible to debugging window. Here is nice explanation of DebuggerTypeProxy.
Pingback: Dew Drop – January 12, 2011 | Alvin Ashcraft's Morning Dew