65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 PropertyGrid

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.66/5 (41投票s)

2008 年 1 月 6 日

CPOL

3分钟阅读

viewsIcon

217549

downloadIcon

6376

一篇关于在 .NET 中使用 PropertyGrid 的文章

引言

PropertyGrid 是一个标准的 Windows 窗体组件,它存在于 .NET Framework 的第一和第二版中。这个组件允许我们显示几乎所有类型的对象的属性,而无需编写任何额外的代码。 对于所有的演示,我创建了一个窗体,并在窗体上放置了一个按钮,按钮的名称是 btnAssign,还有一个名称为 prpGPropertyGrid

最简单的方法

在最简单的情况下,您通过使用属性编写您的类,将它们放在窗体 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;

运行并编译该项目,然后按下该按钮。您将看到类似以下的内容:

result

在 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; }
    }
}

那么我们会得到这个屏幕

result

更复杂或更好看的 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,并覆盖 ConvertToConvertFrom。 然后你会看到如下结果

result

改变标准的显示方式

想象一下,您希望列表中有超过两项,并且布尔类型是不够的。 那么你该怎么办? 首先,您需要为 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; }
    }
}

所以,结果应该像这样

result

显示图片

有时会出现这样一种情况,您想在 PropertyGrid 中显示图片。 假设我们想在游戏中显示 Stonescissorspaper。 那么首先,我们需要组织什么? 我们需要组织 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;
        }
    }
}

完成所有这些之后,您将看到

result

最后

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

© . All rights reserved.