Using PropertyGrid in .NET


Using PropertyGrid control in .NET

PropertyGrid is a standard component in Windows forms, that is in both 1.0 and 2.0 versions of the .NET Framework. This component allows the developer to display properties for nearly all types of objects without writing any additional code.

The Simplest Example

In the simplest case you write a class with properties related to an object, such as a button, and displaying them in a PropertyGrid on a form.

Start a new Windows project, place a button and PropertyGrid on a new Form. In the property editor name the button "btnAssign" and the PropertyGrid, "prpG". After this add a new class to the solution with the following code:

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

    }
}


After this, double-click on the button, and write the following code:

 

PropertyGridSimpleDemoClass pgdc = new PropertyGridSimpleDemoClass();

prpG.SelectedObject = pgdc;

Compile and run the project and then click the button. You'll see something like this:


 
Using Attributes in PropertyGrid

In System.ComponentModel there are some useful attributes such as:

[Browsable(bool)] "To show property or not"
[ReadOnly(bool)] "Ability to edit property"
[Category(string)] "Group of property"
[Description(string)] "Property description, which can be a hint"
[DisplayName(string)] "Display Name property"

If using this class for assigning to SelectedObject:

class PropertyGridSimpleDemoClass2

{

    int m_DisplayInt = 50; // some initialization

    [Browsable(true)] //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(true)] //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 dring or not to drink")] // that is a question

    public bool DisplayBool

    {

        get { return m_DisplayBool; }

        set { m_DisplayBool = value; }

    } 

    Color m_DisplayColors;

    [Browsable(false)]//this property shoul be hided;

    public Color DisplayColors

    {

        get { return m_DisplayColors; }

        set { m_DisplayColors = value; }

    } 

}

then we receive this screen:


 
Enhancing Display Names and Boolean Values

Sometimes it is not enough to display just text values. Sometimes we need to display more helpful information. For example, we might want to say "To drink or not to drink" with possible values of "Yes" and "Yes, of course". In this situation we need to use attribute TypeConverter. First we need to create two additional classes. Name the first "PropertyGridSimpleDemoClass3" and the second "DrinkerClassConverter". Here is the code you should copy:

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";

    }

}

Using Attribute TypeConverter in our BooleanConverter and overriding ConvertTo and ConvertFrom, you see results like this: 


 
Changing Default Display

Imagine that you want more than two items in list. The boolean type is not enough; you need to set Description attributes with a name for every element in enum.

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

}

In another class you need to utilize the type EnumConverter.

 

class DrinkDosesConverter : EnumConverter

{

    private Type _enumType;

    /// Initializing instance

    /// type Enum

    ///this is only one function, that you must

    ///to 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);

    }

}

Third, you need set the attribute TypeConverter for displaying the property.

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

    }

}

So, result should look something like: 


 
Displaying Pictures

Sometimes you have a situation when you want to display pictures in PropertyGrid. Assume we want to show like the game Stone, Scissors, Paper. First, we need to organize the enum with an attribute description for ever member:

enum GameValues

{

    [Description("Stone")]

    Stone,

    [Description("Scissors")]

    Scissors,

    [Description("Paper")]

    Paper

}

 

After this update EnumTypeConverter, converting to a string and assigning to the Description attribute.

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);

    }

}

Similar to the previous step, you need to add to the class an element which inherits 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);

    }

}

and then Bind it using Editor attribute to make it an editable field:

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;

        }

    }

}

After all this you'll see: