Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display Properties in Custom Order in Visual Studio Debugger

In Visual Studio, is it possible to customize the order in which properties are displayed when inspected in the debugger?

Here's an example of a class where I'd really like StartDate and EndDate to appear next to each other, even though they are separated alphabetically .

example

Other debugger options are customizable through attributes like the DebuggerDisplayAttribute, so I was hopeful that another such attribute would exist for DisplayOrder.

[DebuggerDisplay("{Name}")]
public class Rule
{
    public string Name;
    public int MaxAge;
    public DateTime StartDate;
    public DateTime EndDate;
}

In an ideal world, I'd like the ability to order the properties in the inspector in the order that I have defined on the class (even if that requires setting an debugger order attribute incrementally on each property) so the display would look like this:

ideal debugger

like image 878
KyleMit Avatar asked Feb 13 '15 17:02

KyleMit


2 Answers

Pinnable Properties in VS2019+ are a way to go currently, since you can pin your properties in correct order using the pin button inside the datatip.

However, in a more general, reusable approach, the [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] attribute can be used on an array of ordered property name and value pairs. This enables the debug view to 'flatten' the array root, listing the properties in correct order.

Further expanding on KyleMit's answer, and using reflection to load the list of members and fields, makes such a debug view reusable:

[DebuggerDisplay("{Name}")]
[DebuggerTypeProxy(typeof(OrderedPropertiesView))]
public class Rule
{
    public string Name;
    public int MaxAge;
    public DateTime StartDate;
    public DateTime EndDate;        
}

public class OrderedPropertiesView
{
    [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
    public SimpleProperty[] Properties { get; }

    public OrderedPropertiesView(object input)
    {
        this.Properties = input.GetType()
            .GetFields(BindingFlags.Public | BindingFlags.Instance)
            .Select(prop => new SimpleProperty(prop, input))
            .ToArray();
    }

    [DebuggerDisplay("{Value}", Name = "{PropertyName,nq}")]
    public class SimpleProperty
    {
        public SimpleProperty(MemberInfo member, object input)
        {
            this.Value = GetValue(member, input);
            this.PropertyName = member.Name;
        }

        private object GetValue(MemberInfo member, object input)
        {
            switch (member)
            {
                case FieldInfo fi: return fi.GetValue(input);
                case PropertyInfo pi: return pi.GetValue(input);
                default: return null;
            }
        }

        public object Value { get; internal set; }
        public string PropertyName { get; internal set; }
    }
}

Which looks like this in the debugger:

datatip with ordered 'properties'

The order of properties and fields might not be guaranteed with reflection, but since the view is only for debugging purposes, it should be enough. If not, Properties array can be constructed manually, limiting the reusability. In either case, Properties are not really properties, therefore expanding the SimpleProperty datatip looks like this:

expanded datatip showing PropertyName and Value properties of SimpleProperty

Notice that the property inspector magnifyng glass can only be used in the expanded SimpleProperty.Value, which might be an inconvenience.

like image 186
David Cholt Avatar answered Sep 28 '22 08:09

David Cholt


Just to run out the ball on JCL's suggestion to use #if DEBUG with a caluclated property. If you wanted some extra information in the debugger, you could add a field only when in debug mode like this:

[DebuggerDisplay("{Name}")]
public class Rule
{
    public string Name;
    public int MaxAge;
    public DateTime StartDate;
    public DateTime EndDate;

#if DEBUG
    private string DateRange
    {
        get { return StartDate.ToString("dd/MM/yyyy") + " - "+
                     EndDate.ToString("dd/MM/yyyy");
        } 
    }
#endif

}

Which would look like this:

debugger

This gets information presented together, but will still add noise to the inspector.

like image 37
KyleMit Avatar answered Sep 28 '22 06:09

KyleMit