在 Windows 表单的 属性 网格中正确显示自定义对象列表
Properly display list of custom objects in Windows Forms' Property Grid
我正在尝试在属性网格中显示我的墙 class 的属性。现在我的墙 class 看起来像这样:
[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*/
}
当我在 属性 网格上显示 WallType 的实例时,结果如下:
除了成员属性,一切看起来都不错。现在它所做的只是显示容量和计数。我想要的是它显示列表中每个成员的名称 属性。我认为将 ExpandableObjectConverter 放在列表上可以解决问题,但事实并非如此。所以我尝试将自定义 TypeConverter 放在 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*/
}
最后,我的自定义类型转换器
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);
}
}
}
自定义类型转换器有一些效果,因为当我单击按钮手动显示集合时,所有内容都已正确列出。但我想要的是显示名称而不是容量和数量。
正如 Plutonix 评论的那样,这很不寻常,但这里有一种方法可以做到。
只需在成员 属性 上使用以下 TypeConverter。由于 PropertyGrid 基于属性,您必须创建代表每个成员的假属性。这就是此处代表 MemberDescriptor class 的内容。
public class MyTypeConverter : TypeConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType != typeof(string))
return base.ConvertTo(context, culture, value, destinationType);
List<Member> members = value as List<Member>;
if (members == null)
return "-";
return string.Join(", ", members.Select(m => m.Name));
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
List<PropertyDescriptor> list = new List<PropertyDescriptor>();
List<Member> members = value as List<Member>;
if (members != null)
{
foreach (Member member in members)
{
if (member.Name != null)
{
list.Add(new MemberDescriptor(member, list.Count));
}
}
}
return new PropertyDescriptorCollection(list.ToArray());
}
private class MemberDescriptor : SimplePropertyDescriptor
{
public MemberDescriptor(Member member, int index)
: base(member.GetType(), index.ToString(), typeof(string))
{
Member = member;
}
public Member Member { get; private set; }
public override object GetValue(object component)
{
return Member.Name;
}
public override void SetValue(object component, object value)
{
Member.Name = (string)value;
}
}
}
作为奖励,我还添加了到字符串的转换,因此它看起来更好:
我正在尝试在属性网格中显示我的墙 class 的属性。现在我的墙 class 看起来像这样:
[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*/
}
当我在 属性 网格上显示 WallType 的实例时,结果如下:
除了成员属性,一切看起来都不错。现在它所做的只是显示容量和计数。我想要的是它显示列表中每个成员的名称 属性。我认为将 ExpandableObjectConverter 放在列表上可以解决问题,但事实并非如此。所以我尝试将自定义 TypeConverter 放在 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*/
}
最后,我的自定义类型转换器
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);
}
}
}
自定义类型转换器有一些效果,因为当我单击按钮手动显示集合时,所有内容都已正确列出。但我想要的是显示名称而不是容量和数量。
正如 Plutonix 评论的那样,这很不寻常,但这里有一种方法可以做到。
只需在成员 属性 上使用以下 TypeConverter。由于 PropertyGrid 基于属性,您必须创建代表每个成员的假属性。这就是此处代表 MemberDescriptor class 的内容。
public class MyTypeConverter : TypeConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType != typeof(string))
return base.ConvertTo(context, culture, value, destinationType);
List<Member> members = value as List<Member>;
if (members == null)
return "-";
return string.Join(", ", members.Select(m => m.Name));
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
List<PropertyDescriptor> list = new List<PropertyDescriptor>();
List<Member> members = value as List<Member>;
if (members != null)
{
foreach (Member member in members)
{
if (member.Name != null)
{
list.Add(new MemberDescriptor(member, list.Count));
}
}
}
return new PropertyDescriptorCollection(list.ToArray());
}
private class MemberDescriptor : SimplePropertyDescriptor
{
public MemberDescriptor(Member member, int index)
: base(member.GetType(), index.ToString(), typeof(string))
{
Member = member;
}
public Member Member { get; private set; }
public override object GetValue(object component)
{
return Member.Name;
}
public override void SetValue(object component, object value)
{
Member.Name = (string)value;
}
}
}
作为奖励,我还添加了到字符串的转换,因此它看起来更好: