使用 PropertyGrid
一篇关于在 .NET 中使用 PropertyGrid 的文章
引言
PropertyGrid
是一个标准的 Windows 窗体组件,它存在于 .NET Framework 的第一和第二版中。这个组件允许我们显示几乎所有类型的对象的属性,而无需编写任何额外的代码。 对于所有的演示,我创建了一个窗体,并在窗体上放置了一个按钮,按钮的名称是 btnAssign
,还有一个名称为 prpG
的 PropertyGrid
。
最简单的方法
在最简单的情况下,您通过使用属性编写您的类,将它们放在窗体 PropertyGrid
上,并将它的 SelectedObject
属性绑定到该对象的引用。然后您可以在 PropertiesGrid
中查找对象的任何类型的属性,并在 buttonClick
中编写如下代码:
PropertyGridSimpleDemoClass pgdc = new PropertyGridSimpleDemoClass();
prpG.SelectedObject = pgdc;
因此,启动一个新的 Windows 项目,在窗体上放置一个按钮和一个 PropertyGrid
。在属性编辑器中,将 btnAssig
名称分配给按钮,并将 prpG
名称分配给 PropertyGrid
。之后,向解决方案添加一个新的类,代码如下:
class PropertyGridSimpleDemoClass
{
int m_DisplayInt;
public int DisplayInt
{
get { return m_DisplayInt; }
set { m_DisplayInt = value; }
}
string m_DisplayString;
public string DisplayString
{
get { return m_DisplayString; }
set { m_DisplayString = value; }
}
bool m_DisplayBool;
public bool DisplayBool
{
get { return m_DisplayBool; }
set { m_DisplayBool = value; }
}
Color m_DisplayColors;
public Color DisplayColors
{
get { return m_DisplayColors; }
set { m_DisplayColors = value; }
}
}
之后,双击该按钮并编写以下代码:
PropertyGridSimpleDemoClass pgdc = new PropertyGridSimpleDemoClass();
prpG.SelectedObject = pgdc;
运行并编译该项目,然后按下该按钮。您将看到类似以下的内容:

在 PropertyGrid 中使用属性
在 System.ComponentModel
中,有一些有用的属性。例如:
[Browsable(bool)] – to show property or not
[ReadOnly(bool)] – possibility to edit property
[Category(string)] – groups of property
[Description(string)] – property description. It is something like a hint.
[DisplayName(string)] – display property
如果您使用此类来分配给 SelectedObject
class PropertyGridSimpleDemoClass2
{
int m_DisplayInt = 50; // some initialization
[Browsable(bool)] // this property should be visible
[ReadOnly(true)] // but just read only
[Description("sample hint1")] // sample hint1
[Category("Category1")] // Category that I want
[DisplayName("Int for Displaying")] // I want to say more, than just DisplayInt
public int DisplayInt
{
get { return m_DisplayInt; }
set { m_DisplayInt = value; }
}
string m_DisplayString;
[Browsable(bool)] // this property should be visible
[ReadOnly(false)] // this property is for editing
[Description("Example Displaying hint 2")] // sample hint2
[Category("Category1")] // Category that I want
[DisplayName("Name")] // and more than Display String
public string DisplayString
{
get { return m_DisplayString; }
set { m_DisplayString = value; }
}
bool m_DisplayBool;
[Category("Category2")] // Category that I want
[Description("To be or not to be")] // yet one hint
[DisplayName("To drink or not to drink")] // that is a question
public bool DisplayBool
{
get { return m_DisplayBool; }
set { m_DisplayBool = value; }
}
Color m_DisplayColors;
false)> //this property should be hidden;
public Color DisplayColors
{
get { return m_DisplayColors; }
set { m_DisplayColors = value; }
}
}
那么我们会得到这个屏幕

更复杂或更好看的 True/False 值 - 对于 Bool 值尤其如此
但有时,仅仅显示文本值是不够的。有时,我们需要更令人印象深刻的显示。例如,如果我们想将 “喝还是不喝” 中的变体更改为其他变体。 比如:“是的” 和 “当然,是的”。 那么我们需要使用 TypeConverter
属性。 首先,我们应该创建两个额外的类。 第一个我们将命名为 PropertyGridSimpleDemoClass3
,另一个命名为 DrinkerClassConverter
。 看一下下面给出的类
class PropertyGridSimpleDemoClass3
{
bool m_DrinkOrNot;
[DisplayName("Drink or not")]
[Description("Drink or not")]
[Category("Make right decision")]
[TypeConverter(typeof(DrinkerClassConverter))]
public bool DrinkOrNot
{
get { return m_DrinkOrNot; }
set { m_DrinkOrNot = value; }
}
}
class DrinkerClassConverter : BooleanConverter
{
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture,
object value,
Type destType)
{
return (bool)value ?
"Yes" : "Yes, of course";
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture,
object value)
{
return (string)value == "Yes";
}
}
所有需要做的就是使用属性 TypeConverter
,继承 BooleanConverter
,并覆盖 ConvertTo
和 ConvertFrom
。 然后你会看到如下结果

改变标准的显示方式
想象一下,您希望列表中有超过两项,并且布尔类型是不够的。 那么你该怎么办? 首先,您需要为 enum
中的每个元素设置具有所需名称的 Description
属性。
enum DrinkDoses
{
[Description("Half of litre")]
litre,
[Description("One litre")]
oneLitre,
[Description("Two litres")]
twoLitre,
[Description("Three litres")]
threeLitres,
[Description("Four litres")]
fourLitres,
[Description("Death dose, five litres")]
fiveLitres
}
其次,您需要实现 EnumConverter
类型。
class DrinkDosesConverter:EnumConverter
{
private Type _enumType;
/// Initializing instance
/// type Enum
/// this is only one function, that you must
/// change. All another functions for enums
/// you can use by Ctrl+C/Ctrl+V
public DrinkDosesConverter(Type type)
: base(type)
{
_enumType = type;
}
public override bool CanConvertTo(ITypeDescriptorContext context,
Type destType)
{
return destType == typeof(string);
}
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture,
object value, Type destType)
{
FieldInfo fi = _enumType.GetField(Enum.GetName(_enumType, value));
DescriptionAttribute dna =
(DescriptionAttribute)Attribute.GetCustomAttribute(
fi, typeof(DescriptionAttribute));
if (dna != null)
return dna.Description;
else
return value.ToString();
}
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type srcType)
{
return srcType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture,
object value)
{
foreach (FieldInfo fi in _enumType.GetFields())
{
DescriptionAttribute dna =
(DescriptionAttribute)Attribute.GetCustomAttribute(
fi, typeof(DescriptionAttribute));
if ((dna != null) && ((string)value == dna.Description))
return Enum.Parse(_enumType, fi.Name);
}
return Enum.Parse(_enumType, (string)value);
}
}
最后,您需要设置 TypeConverter
属性来显示属性。
class DrinkerDoses
{
DrinkDoses m_doses;
[DisplayName("Doses")]
[Description("Drinker doses")]
[Category("Alcoholics drinking")]
[TypeConverter(typeof(DrinkDosesConverter))]
public DrinkDoses Doses
{
get
{
return m_doses;
}
set
{
m_doses = value;
}
}
int m_dataInt;
public int DataInt
{
get { return m_dataInt; }
set { m_dataInt = value; }
}
}
所以,结果应该像这样

显示图片
有时会出现这样一种情况,您想在 PropertyGrid
中显示图片。 假设我们想在游戏中显示 Stone
,scissors
,paper
。 那么首先,我们需要组织什么? 我们需要组织 enum
,为每个需要的成员设置 Description
属性
enum GameValues
{
[Description("Stone")]
Stone,
[Description("Scissors")]
Scissors,
[Description("Paper")]
Paper
}
之后,我们实现 EnumTypeConverter
,它通过使用 Description
属性转换为 string
。
class GameValuesConverter: EnumConverter
{
private Type _enumType;
public GameValuesConverter(Type type)
: base(type)
{
_enumType = type;
}
public override bool CanConvertTo(ITypeDescriptorContext context,
Type destType)
{
return destType == typeof(string);
}
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture,
object value, Type destType)
{
FieldInfo fi = _enumType.GetField(Enum.GetName(_enumType, value));
DescriptionAttribute dna =
(DescriptionAttribute) Attribute.GetCustomAttribute(
fi, typeof(DescriptionAttribute));
if (dna != null)
return dna.Description;
else
return value.ToString();
}
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type srcType)
{
return srcType == typeof (string);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture,
object value)
{
foreach (FieldInfo fi in _enumType.GetFields())
{
DescriptionAttribute dna =
(DescriptionAttribute) Attribute.GetCustomAttribute(
fi, typeof(DescriptionAttribute));
if ((dna != null) && ((string)value == dna.Description))
return Enum.Parse(_enumType, fi.Name);
}
return Enum.Parse(_enumType, (string) value);
}
}
这看起来与上一步非常相似。 我将向您展示另一个示例。 现在我们需要组织包含继承 UITypeEditor
的元素的类
class GameEditor: UITypeEditor
{
public override bool GetPaintValueSupported(ITypeDescriptorContext context)
{
return true;
}
public override void PaintValue(PaintValueEventArgs e)
{
string whatImage = e.Value.ToString();
whatImage += ".bmp";
//getting picture
Bitmap bmp = (Bitmap)Bitmap.FromFile(whatImage);
Rectangle destRect = e.Bounds;
bmp.MakeTransparent();
//and drawing it
e.Graphics.DrawImage(bmp, destRect);
}
}
并使用 Editor
属性将其绑定到可编辑的字段
class GameClassDisplayer
{
GameValues m_GameValues;
[DisplayName("Choose your variant")]
[Description("You can choose between Stone, scissors, paper")]
[Category("Choosing")]
[Editor(typeof(GameEditor), typeof(UITypeEditor))]
public GameValues DisplayGameValues
{
get
{
return m_GameValues;
}
set
{
m_GameValues = value;
}
}
}
完成所有这些之后,您将看到

最后
我希望听到您的评论,并回答您的问题。