I'm trying to display the properties of my Wall class in a properties grid. Right now my wall class looks like this:
[TypeConverter(typeof(ExpandableObjectConverter))]
public class WallType
{
#region _private variables and Properties
public string Name { get; set; } //Name of the wall to identify it. For the wall program, it is automatically generated (ex. E4-1 where E = exterior wall, 4 = 2x4, and 1 means first exterior wall defined)
public string Type { get; set; } //Type of wall (exterior, interior, etc.)
public bool IsMirrorable { get; set; } //Identifies if the wall is mirrorable or not
public Distance Height { get; set; } //Height of the wall from bottom to talk
public string StudPattern { get; set; } //Pattern in which the studs of the wall are laid out
public Distance StudSpacing { get; set; } //Spacing between each stud
public VaporBarrier Barrier { get; set; }
public LetIn LetInOfWall { get; set; }
public Sheathing SheathingUsed { get; set; }
[TypeConverter(typeof(ExpandableObjectConverter))]
public List<Member> Members { get; set; }
public bool HasLetIn { get; set; }
public bool HasCapPlate { get; set; }
public bool HasVaporBarrier { get; set; }
public bool HasSheathing { get; set; }
#endregion
/*Constructors and Methods not shown*/
}
When I display an instance of WallType on the property grid it comes out like this:
Everything looks good EXCEPT the Members property. Right now all it does is display the capacity and count. What I want is for it to display the names property of each member in the list. I thought that putting an ExpandableObjectConverter over the list would solve the problem but it didnt. So I tried putting a custom TypeConverter on top of the Member class
[TypeConverter(typeof(MemberObjectConverter))]
public class Member
{
#region _private variables and Properties
public string Name { get; set; } //Name of the member used to uniquely identify it
public string Size { get; set; } //Size of the member (ex. 2x4)
public string Grade { get; set; } //Grade of the wood used to make the member
public Distance Length { get; set; } //Length of the member
public string Species { get; set; } //Type of wood the member is made of
public string Treatment { get; set; } //Type of treatment applied to the member
public string Other { get; set; } //Variable for other notes about the member
#endregion
/*Constructors and Methods not shown*/
}
And finally, my custom type converter
namespace Wall_Program
{
/// <summary>
/// Extending the ExpandableObjectConverter to display member objects properly
/// </summary>
public class MemberObjectConverter : ExpandableObjectConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(Member))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, System.Type destinationType)
{
if (destinationType == typeof(System.String) && value is Member)
{
Member m = (Member)value;
return "Name: " + m.Name +
", Size: " + m.Size +
", Grade: " + m.Grade +
", Length: " + m.Length.Architectural +
", Species: " + m.Species +
", Treatment: " + m.Treatment +
", Other: " + m.Other;
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
try
{
string s = (string)value;
int colon = s.IndexOf(':');
int comma = s.IndexOf(',');
if (colon != -1 && comma != -1)
{
string name = s.Substring(colon + 1, (comma - colon - 1));
colon = s.IndexOf(':', comma + 1);
comma = s.IndexOf(',', comma + 1);
string size = s.Substring(colon + 1, (comma - colon - 1));
colon = s.IndexOf(':', comma + 1);
comma = s.IndexOf(',', comma + 1);
string grade = s.Substring(colon + 1);
colon = s.IndexOf(':', comma + 1);
comma = s.IndexOf(',', comma + 1);
Distance length = new Distance(s.Substring(colon + 1, (comma - colon - 1)));
colon = s.IndexOf(':', comma + 1);
comma = s.IndexOf(',', comma + 1);
string species = s.Substring(colon + 1, (comma - colon - 1));
colon = s.IndexOf(':', comma + 1);
comma = s.IndexOf(',', comma + 1);
string treatment = s.Substring(colon + 1, (comma - colon - 1));
colon = s.IndexOf(':', comma + 1);
string other = s.Substring(colon + 1);
Member m = new Member(name, size, grade, length, species, treatment, other);
return m;
}
}
catch
{
throw new ArgumentException("Can not convert '" + (string)value + "' to type Member");
}
}
return base.ConvertFrom(context, culture, value);
}
}
}
The custom type converter is having some effect because when I click the button to manually show the collection everything is listed properly. But what I want is for the names to be what is shown instead of the capacity and count.
This is rather unusual as Plutonix commented, but here is a way to do it.
Just use the following TypeConverter on the Members property. Since the PropertyGrid is based on properties, you have to create fake properties that represent each member. That's what represents the MemberDescriptor class here.
As a bonus, I have also added the conversion to string so it looks nicer: