Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C#

PropertyGrid Utilities

Rate me:
Please Sign up or sign in to vote.
4.90/5 (44 votes)
7 Jan 2016CPOL4 min read 250.9K   7.8K   153   75
An article on sorting and globalization of properties in a PropertyGrid

Image 1

Image 2

Introduction

This article is on sorting and globalization of properties in a PropertyGrid.

Features:

  • Declarative (static) definition of property behaviour: property order, DisplayName, globalization of DisplayName, Description, and Category.
  • Dynamic definition of property behaviour using a callback. For each property, a property attributes provider can be set in which all attributes mentioned above can be overridden. Also, IsBrowsable and IsReadOnly can be modified at runtime.
  • Use the Tab key for tabbing through items.
  • Globalized tool tips of tool bar buttons.

It's easy to use and has ways to modify the place it looks for resources.

Background

I ripped code from two projects, fused and improved them. The sorting idea was ripped from Paul Tingey, his PropertySorter class. Globalization was ripped from Globalized Property Grid by Gerd Klevesaat. He, however, used inheritance from GlobalizationObject, which makes the mechanism not so flexible. Plus, his implementation was very slow (especially in combination with sorting), and didn't include Category globalization. The dynamic behaviour was developed by George Soules and me.

Using the Code

The example below basically shows most of the possibilities:

C#
using System;
using System.ComponentModel;

using PropertyGridUtils;

namespace MyNameSpace
{
    [TypeConverter(typeof(PropertiesDeluxeTypeConverter))]
    class SomeClassWithProperties {
        string lastName = "Zeeuw";
        string occupation = "Software engineer";

        int dynamicProperty = 3;
        string dynamicPropertyDisplayName = "Dynamic property";
        bool dynamicPropertyIsVisible = true;
        bool dynamicPropertyIsReadOnly = false;

        string propertyWithResourcesElseWhere = "Dummy";
        SomeClassWithProperties child;

        // DisplayName and Description are gotten from resource file
        // MyNameSpace.SomeClassWithProperties.resources.
        [PropertyOrder(0)]
        [GlobalizedProperty(CategoryId = "GlobalCategory")]
        public string LastName {
            get {
                return lastName;
            }
        }

        // DisplayName and Description are gotten from resource file
        // MyNameSpace.SomeClassWithProperties.resources.
        [PropertyOrder(1)]
        [GlobalizedProperty(CategoryId = "GlobalCategory")]
        public string Occupation {
            get {
                return occupation;
            }
        }

        // Behaviour of this property is partially defined runtime.
        [PropertyOrder(2)]
        [Description("This property is partially dynamic.")]
        [PropertyAttributesProvider("DynamicPropertyAttributesProvider")]
        public int DynamicProperty {
            get {
                return dynamicProperty;
            }
            set {
                dynamicProperty = value;
            }
        }

        public void DynamicPropertyAttributesProvider(PropertyAttributes attributes) {
            attributes.DisplayName = dynamicPropertyDisplayName;
            attributes.IsReadOnly = dynamicPropertyIsReadOnly;
            attributes.IsBrowsable = dynamicPropertyIsVisible;
        }

        [PropertyOrder(3)]
        [DisplayName("Dynamic property display name")]
        public string DynamicPropertyDisplayName {
            get {
                return dynamicPropertyDisplayName;
            }
            set {
                dynamicPropertyDisplayName = value;
            }
        }

        [PropertyOrder(4)]
        [DisplayName("Dynamic property visible")]
        public bool DynamicPropertyIsVisible {
            get {
                return dynamicPropertyIsVisible;
            }
            set {
                dynamicPropertyIsVisible = value;
            }
        }

        [PropertyOrder(5)]
        [DisplayName("Dynamic property readonly")]
        public bool DynamicPropertyIsReadOnly {
            get {
                return dynamicPropertyIsReadOnly;
            }
            set {
                dynamicPropertyIsReadOnly = value;
            }
        }

        // DisplayName and Description are gotten from resource file
        // SomeOtherResources.resources
        [PropertyOrder(6)]
        [GlobalizedProperty(
                            BaseName = "SomeOtherResources",
                            DisplayNameId = "Dummy.DisplayName",
                            DescriptionId = "Dummy.Description"
                            )]
        public string PropertyWithResourcesElseWhere {
            get {
                return propertyWithResourcesElseWhere;
            }
        }

        // Property with sub properties for testing
        // tabbing accross expanded/non expanded items.
        [PropertyOrder(7)]
        public SomeClassWithProperties Child {
            get {
                if (child == null) {
                    child = new SomeClassWithProperties();
                }
                return child;
            }
        }
     }
}

Static Usage

Property behaviour is defined using the attributes DisplayNameAttribute, DescriptionAttribute, GlobalizedPropertyAttribute, GlobalizedTypeAttribute, and PropertyOrderAttribute.

Usage without resources is simple, users can use the DisplayNameAttribute and DescriptionAttribute to directly set the DisplayName and Description. But when working like this, the properties are not globalized.

Using resource files enables globalization, but is slightly more complicated. By default, resources are gotten from a file namespace.class.resources. This can, if needed, be controlled with the GlobalizedPropertyAttribute or GlobalizedTypeAttribute. The resource for DisplayName is gotten from the ResourceManager from the resource string SomeProperty.DisplayName. Similarly, the Description is gotten from SomeProperty.Description. This can also be controlled through the GlobalizedPropertyAttribute though, so, e.g., description strings can be reused in multiple properties/classes. Category is, by default, gotten from SomeProperty.Category. To reuse a category string in a resource file, you can specify [GlobalizedProperty(CategoryId = "GlobalCategory")] (thanks to Paul Tingey for the idea).

Dynamic Usage

It is possible to define a PropertyAttributesProviderAttribute at a property. The method name mentioned there will be called by the PropertiesDeluxeTypeConverter with a PropertyAttributes object as the only parameter. The method can then change all attributes there like IsBrowsable, IsReadOnly, DisplayName, etc. The PropertyGrid must be refreshed (Refresh()) for the changes to take effect.

Tabbing Through Properties

To make use of the Tab key feature and globalized tool tips for the tool bar buttons, use CustomPropertyGrid. It also has a property ExpandOnTab which sets whether to expand an expandable item when Tab is pressed. Note that pressing the Enter key also achieves that.

Points of Interest

If no resource file is there for the neutral culture, globalization is disabled. But still, PropertiesDeluxeTypeConverter can then be used for sorting and setting the DisplayName of properties.

One could set up the resources to, e.g., use only one resource file. There is no protection from name clashes then though. The namespace could be prepended to the resource names, but then the names would get very long.

For adding languages for the tool tips, compile satellite assemblies containing the PropertyGridUtils.CustomPropertyGrid.<language>.resources file (don't forget signing and giving it the correct assembly version, see PropertyGridUtils\compileResources.bat). The assembly must be located in a <language> subdir, like the nl example.

New Status Bar Class (.NET 1.1 Only)

I also wrote a small status bar class because the standard .NET 1.1 status bar doesn't allow embedding of controls (the .NET 2.0 StatusStrip is more powerful). It's not much code so it's probably not worth devoting a separate article to it (and there's already one somewhere on CodeProject doing something similar). One feature that is in this version that I like is that both absolute and relative widths can be mixed in the status bar items. E.g., two items can be defined, one of absolute width 50 pixels, relative width 70%, and the other item 200 pixels, relative width 30%. If the total status bar width is 1000 pixels, then 200 + 50 = 250 pixels are fixed. The remaining 750 pixels are divided according to the relative sizes.

History

  • 7th April 2006: Migrated to .NET 2.0 (DisplayNameAttribute is now part of the .NET libraries)
  • 21st June 2005: Improved tabbing to go over all expanded items instead of items on one level (1.0.0.3). Also added a new status bar class.
  • 19th October 2004: Cooperated with George Soules to add dynamic behaviour. (Assembly version is now 1.0.0.2.)
  • 12th September 2004: Added Tab key feature and globalized tool tips for tool bar buttons
  • 21st July 2004: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Netherlands Netherlands
Wout works as a freelancer based in the Netherlands. His interests are .NET, 3D, DWG, DXF, OpenGL, Computational Geometry and playing Tank Ball!

One of his latest private projects is a commercial DWG DXF .NET library and small DWG DXF viewing application.

Comments and Discussions

 
GeneralCategory Localisation doesn't work when using multiple SelectedObjects Pin
Jerleth.15-May-06 0:53
Jerleth.15-May-06 0:53 
GeneralRe: Category Localisation doesn't work when using multiple SelectedObjects Pin
wout de zeeuw15-May-06 7:07
wout de zeeuw15-May-06 7:07 
GeneralRe: Category Localisation doesn't work when using multiple SelectedObjects Pin
Stuart Wells4-Jul-06 5:24
Stuart Wells4-Jul-06 5:24 
AnswerSolution: Category Localisation doesn't work when using multiple SelectedObjects Pin
Niels.Bos9-May-07 2:44
Niels.Bos9-May-07 2:44 
GeneralRe: Solution: Category Localisation doesn't work when using multiple SelectedObjects Pin
Len202025-Feb-08 9:48
Len202025-Feb-08 9:48 
GeneralLocalize the child property's names in PropertyGrid. Pin
krzychub7-Jan-06 6:55
krzychub7-Jan-06 6:55 
GeneralRe: Localize the child property's names in PropertyGrid. Pin
wout de zeeuw7-Jan-06 7:41
wout de zeeuw7-Jan-06 7:41 
GeneralRe: Localize the child property's names in PropertyGrid. Pin
krzychub7-Jan-06 9:09
krzychub7-Jan-06 9:09 
Hi Wout. Thanks for Your quick reply that
rather... makes me sad Frown | :-(
Have You got any ideas to solve my problem?
I only want to display localized property names in
PropertyGrid (including child properties names).

Once again Thanks for Your reply.
Krzychub.
GeneralRe: Localize the child property's names in PropertyGrid. Pin
wout de zeeuw7-Jan-06 23:42
wout de zeeuw7-Jan-06 23:42 
QuestionHow to control the height of the PropertyGrid comment area Pin
dsk303719-Dec-05 9:12
dsk303719-Dec-05 9:12 
AnswerRe: How to control the height of the PropertyGrid comment area Pin
gedri25-Dec-06 6:27
gedri25-Dec-06 6:27 
Generalto show/replace it in the Properties View Pin
gan.gary17-Dec-05 15:43
gan.gary17-Dec-05 15:43 
GeneralRe: to show/replace it in the Properties View Pin
wout de zeeuw17-Dec-05 22:31
wout de zeeuw17-Dec-05 22:31 
GeneralRe: to show/replace it in the Properties View Pin
gan.gary23-Dec-05 15:45
gan.gary23-Dec-05 15:45 
GeneralPerformance Pin
Tuakisan5-Dec-05 19:47
Tuakisan5-Dec-05 19:47 
GeneralPerformance Pin
Tuakisan15-Dec-05 23:20
Tuakisan15-Dec-05 23:20 
GeneralRe: Performance Pin
wout de zeeuw16-Dec-05 8:44
wout de zeeuw16-Dec-05 8:44 
QuestionLicensing? Pin
Eugene Polonsky19-Aug-05 8:36
Eugene Polonsky19-Aug-05 8:36 
AnswerRe: Licensing? Pin
Steve Maier19-Aug-05 9:13
professionalSteve Maier19-Aug-05 9:13 
GeneralRe: Licensing? Pin
Eugene Polonsky19-Aug-05 9:47
Eugene Polonsky19-Aug-05 9:47 
GeneralRe: Licensing? Pin
Steve Maier19-Aug-05 9:56
professionalSteve Maier19-Aug-05 9:56 
GeneralRe: Licensing? Pin
Eugene Polonsky19-Aug-05 9:59
Eugene Polonsky19-Aug-05 9:59 
GeneralRe: Licensing? Pin
Steve Maier19-Aug-05 10:13
professionalSteve Maier19-Aug-05 10:13 
AnswerRe: Licensing? Pin
wout de zeeuw19-Aug-05 22:30
wout de zeeuw19-Aug-05 22:30 
GeneralWorking with mutiples class Pin
CQüeb10-Jun-05 7:54
CQüeb10-Jun-05 7:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.