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” .
class Student
{
public int Roll { get; set; }
public string Name { get; set; }
public Marks Marks { get; set; }
public Address Addresses { get; set; }
}
class Address
{
public string City { get; set; }
public int Pin { get; set; }
}
public class Marks
{
public int Subject1 { get; set; }
public int Subject2 { get; set; }
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.
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.
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 } });
}
}
internal class StudentDebuggerProxy
{
private Student student;
public StudentDebuggerProxy(Student student)
{
this.student = student;
}
public int Roll
{
get
{
return this.student.Roll;
}
}
public string Name
{
get
{
return this.student.Name;
}
}
public string City
{
get
{
return this.student.Addresses.City;
}
}
public int PinNo
{
get
{
return this.student.Addresses.Pin;
}
}
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;
}
}
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
{
public int Roll { get; set; }
public string Name { get; set; }
public Marks Marks { get; set; }
public Address Addresses { get; set; }
}
class Address
{
public string City { get; set; }
public int Pin { get; set; }
}
public class Marks
{
public int Subject1 { get; set; }
public int Subject2 { get; set; }
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.