# Editing a value

The SPG library provides a large set of inplace controls. An inplace control is the component appearing in the right column when a Property containing a value is selected and not disabled.

Assign inplace controls using Property feels. A feel is actually an instance of a class derived from PropertyFeel. The feel creates and correctly positions the inplace control.

# Automatic feel assignment

When a Property is first created, SPG assigns a feel according to a simple order of preference.

# > SPG order of preference for assigning a feel

  1. Property attribute, PropertyFeelAttribute.
  2. Parent Property attribute, PropertyFeelAttribute specifying feel of child.
  3. Property attribute, PasswordPropertyTextAttribute.
  4. Feel registered for the underlying Property type.
  5. UITypeEditor for the underlying Property type.
  6. Set of standard values published by the Property value’s underlying TypeConverter.
  7. Default feel.

For detailed description of each preference see below.

Note

It is also possible to assign a feel at runtime. See Manually assigning a feel.

To assign a Property feel, we use an identifier string as defined in the PropertyGrid class:

Identifier Description
FeelNone No feel is used on the Property.
FeelEdit A simple textbox.
FeelMaskedEdit A masked textbox.
FeelUpDown Up/Down buttons at the right side without textbox.
FeelEditUpDown Up/Down buttons at the right side with a simple textbox.
FeelEditPassword Textbox to edit a password (bullet characters).
FeelList Dropdown list without textbox.
FeelEditList Dropdown list with a textbox.
FeelButton Button at the right side without textbox.
FeelEditButton Button at the right side with a textbox.
FeelFontButton Button at the right side with a textbox. Clicking on the button opens the usual font dialog editor.
FeelDateTime True DateTimePicker control for dates and times.
FeelCheckbox One or multiple checkboxes.
FeelRadioButton Multiple radiobuttons.
FeelTrackbar Trackbar showing the current value on the left.
FeelTrackbarEdit Trackbar showing the current value on the left in a textbox.
FeelMultilineEdit Multi-line textbox.

# > UITypeEditor classes

There is no identifier for UITypeEditor classes because it is impossible to target a specific editor from the range of possible editors. For this reason, the feel for UITypeEditor classes is handled internally.

# PropertyFeelAttribute

Specify the feel for a Property at design-time or at runtime by passing the PropertyFeelAttribute class to methods like AppendProperty.

Example:

[PropertyFeel(PropertyGrid.FeelEditList)]
public string OperatingSystems {

The Property is assigned a combobox.

Note

In this example, this may seem strange to link a string to a list. The listbox can be populated on demand by dynamic data, and the result of the selection is set in the string.

# Parent's PropertyFeelAttribute

Specify the feel for a child Property at design-time or at runtime by passing the PropertyFeelAttribute class to methods like AppendProperty. This approach is useful in Full Reflection Mode and to explicitly decorate an inaccessible child Property.

Example:

[PropertyFeel("MiterLimit", PropertyGrid.FeelEditUpDown)]
[PropertyFeel("Color",PropertyGrid.FeelList)]
[PropertyFeel(PropertyGrid.FeelNone)]
public Pen OutlinePen {

The Property is instructed to assign specific feels to two child Properties: Color, and MiterLimit. We don’t have the code for the Pen class, so this is an easy way to specify their editor - more convenient than writing a new TypeDescriptor and assigning it to the pen.

The Property for the Pen itself has no feel, thanks to the FeelNone identifier. (Note that “null” could have been used instead.)

# PasswordPropertyTextAttribute

The FeelEditPassword identifier assigns a password feel in the usual way. However, if you have an existing class that uses the PasswordPropertyText attribute, the correct feel will be chosen for you.

Good to know

This attribute also assigns the correct look class.

# Feel registered for the Property type

There are two kinds of registration:

# > Assigning a string identifier to a custom feel

A custom feel must be registered before you can use it in the grid. This involves using the RegisterFeel call to assign a string identifier, and then informing the SPG library:

public const string MyFeelIdentifier = "myidentifier";
...
myGrid.RegisterFeel(MyFeelIdentifier, new MyCustomFeel(myGrid));

This creates the feel as a singleton and stores it in the PropertyGrid.

# > Associating a feel with a Property type

You can make a custom or built-in feel the default for new Properties of a specified type (different feels can subsequently be applied). Specify a default association by registering a pair (Property type and feel identifier) with the PropertyGrid class, e.g.

RegisterFeelAttachment(typeof(Font), FeelFontButton);

# UITypeEditor for the property type

If the SPG library fails to find a feel specified for the property or its type (see above), it scans the underlying type of the Property for a UITypeEditor class that can handle the type. It looks for this class in the EditorAttribute placed in the metadata of the property type, then in custom attributes passed to an eventual Append(Managed)Property call. Even if the property type is a nullable type, the editor will be found.

By default, a UITypeEditor shows a textbox. This can be avoided by decorating a property with the UITypeEditorTextBoxVisibility attribute.

If SPG finds a UITypeEditor class with no particular style (GetEditStyle() returns UITypeEditorEditStyle.None), it checks the TypeConverter to confirm that the editor can handle standard values (GetStandardValuesSupported() returns true). This is typically what happens, e.g., for the FontNameEditor class. This class offers a useful way to paint a font name (it draws sample text using the selected font) and also uses a generic list which handles all possible values. In such cases, the SPG library returns a FeelEditList identifier.

# Default feel for lists

On failing to satisfy the above preferences, SPG interrogates the TypeConverter to see if it supports standard values. If this is the case, it returns a FeelEditList identifier.

# Global default feel

Finally, the library returns the default feel. If none is specified, it returns the FeelEdit identifier.

To set the default feel, use the PropertyGrid.DefaultFeel property before filling the grid.

# Manually assigning a feel

You can change the feel of a Property at any time by directly setting the feel of the Property class with the one returned by PropertyGrid.GetRegisteredFeel():

propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelMaskedEdit);

It is possible to avoid inplace controls for a given Property:

propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelNone);
// or
propEnum.Property.Feel = null;

Technically speaking, the feel belongs to a PropertyValue instance. But as shown previously, there is a shortcut to access it directly from the Property class. The following does the same as the above example, but only if the Value instance is non null (and this time we modify the default feel):

propEnum.Property.Value.Feel = null;

# Feels in detail

This section covers feels in detail, including conditions for their use, data types, and how the client code is implemented depending on the mode used to create Properties.

# FeelNone

It is the equivalent of null.

This identifier exists for readability and can be replaced by null. Use it when you do not need an inplace control to appear. The user will be unable to select the text (if any) for copying.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelNone)] // You can also pass null
public string MyProperty {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Label", this, "MyProperty", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelNone);  // or assign null

# FeelEdit

This creates a textbox.

The textbox is the most simple inplace control. It does support clipboard operations. Text boxes displayed by other feels belong to the same textbox class as displayed by this class.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelEdit)]
public string MyProperty {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Label", this, "MyProperty", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelEdit);

# FeelMaskedEdit

This creates an inplace control derived from the MaskedTextBox class.

A PropertyMaskedEditLook must also be assigned. The Mask and the PromptChar properties are set on the look.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelMaskedEdit)]
[PropertyLook(typeof(PropertyMaskedEditLook), "(000) 000-0000"), '_']
public string Editbox11 {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Phone#", this, "Phone", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelMaskedEdit);
propEnum.Property.Look = new PropertyMaskedEditLook("(000) 000-0000", '_');

# FeelEditPassword

This configures a simple textbox so that the string is displayed with bullets.

It must be used in conjunction with the PropertyPasswordLook class.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelEditPassword)]
[PropertyLook(typeof(PropertyPasswordLook))]
public string Password {

As a shortcut, use the PasswordPropertyText attribute defined in the .Net framework:

[PasswordPropertyText(true)]
public string Password {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Password", this, "Password", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelMaskedEdit);
propEnum.Property.Look = new PropertyPasswordLook();

# FeelMultilineEdit

This turns a simple textbox into a multiline textbox. The user can create line breaks by typing Ctrl+Enter (or Enter if EnterCreatesLineBreak is set on PropInPlaceTextbox).

The height is fixed and must be supplied as a multiplier of the basic row height.

This feel must be used in conjunction with the PropertyMultilineEditLook class.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelMultilineEdit)]
[PropertyHeightMultiplier(3)]
[PropertyLook(typeof(PropertyMultilineEditLook))]
public string Editbox3 {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Label", this, "MyProperty", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelMultilineEdit);
propEnum.Property.HeightMultiplier = 3;
propEnum.Property.Look = new PropertyMultilineEditLook();

# FeelUpDown and FeelEditUpDown

This sets up an up/down button to the right of an optional textbox.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelUpDown)]
public string MyProperty {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Label", this, "MyProperty", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelUpDown);

# > Setup

At both design-time and runtime, you can extend the inplace control's possible behaviour by configuring it the moment it appears. Do this during the InPlaceCtrlVisibleEvent event by casting the inplace control to the correct type and calling some properties:

protected override void OnInPlaceCtrlVisible(InPlaceCtrlVisibleEventArgs e)
{
    if (e.PropertyEnum.Property.Id == myId)
        ((PropInPlaceUpDown)e.InPlaceCtrl).RealtimeChange = true;

    base.OnInPlaceCtrlVisible(e);
}
# Available properties include:
Property Description
Increment Increment used when the property is a numerical value.
RealtimeChange If true, the up/down keys immediately change the value.
If false, the value is validated only on leaving the control.
# Using a validator class to set limits

Validator classes can set limits on up/down buttons. SPG comes with a min/max validator for this purpose. Assign it to the Property and the feel will detect it:

[PropertyValidator(typeof(PropertyValidatorMinMax), 0, 200)]
public int MyProperty {
# Property types

The up/down inplace control can be used for:

  • Any Property type that has a set of different possible values (like enumeration, boolean, color, etc).
  • A numerical type. As seen above you can modify the increment.
  • Any other type (like a string) as long as you catch the PropertyUpDownEvent and supply the next value. See the example, below.
# Example (from the sample code)
protected override void OnPropertyUpDown(PropertyUpDownEventArgs e)
{
    if (e.PropertyEnum.Property.Id == 1000)
    {
        e.Value = (Double.Parse(e.Value) + (e.ButtonPressed ==
           PropertyUpDownEventArgs.UpDownButtons.Up ? 0.05 : -0.05)).ToString();
    }
    else if (e.PropertyEnum.Property.Id == 2000)
    {
        int index = upDownString.IndexOf(e.Value);
        try
        {
           e.Value = (string)upDownString[index + (e.ButtonPressed ==
                PropertyUpDownEventArgs.UpDownButtons.Up ? -1 : 1)];
        }
        catch (ArgumentOutOfRangeException) {}
    }
    base.OnPropertyUpDown(e);
}

The first If block is equivalent to setting an increment of 0.05. The second If block changes the value of a string property using dynamic values from the upDownString array.

# FeelList and FeelEditList

This creates an inplace control capable of opening a dropdown listbox and showing an optional textbox.

By default (for the enum, string and boolean types), the listbox is the classic list of strings showing all the possible values of the property.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelEditList)]
public string MyProperty {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Label", this, "MyProperty", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelEdiList);

# > Setup

At both design-time and runtime, you can extend the inplace control's possible behaviour by configuring it the moment it appears. Do this when handling the InPlaceCtrlVisibleEvent event, by casting the inplace control to the correct type and calling some properties:

protected override void OnInPlaceCtrlVisible(InPlaceCtrlVisibleEventArgs e)
{
    if (e.PropertyEnum.Property.Id == myId)
        ((PropInPlaceList)e.InPlaceCtrl).RealtimeChange = true;

    base.OnInPlaceCtrlVisible(e);
}
# Available properties include:
Property Description
PreventResizable SPG determines itself if a resizable glyph has to be shown or not depending on the size of the content in the list. By setting this property to true, you can prevent this to happen and no resizable box will be visible.
RealtimeChange If true, the up/down keys immediately change the value.
If false, the value is validated only on leaving the control.

The PropInPlaceList.MinDropDownHeight static variable also allows you to control the minimum height of the dropdown part of lists. PropInPlaceList.MaxDropDownWidth on its side controls the maximum width of the dropdown.

# Property types

The listbox inplace control can be used for:

  • Any Property type that has a set of different possible values (like enumeration, boolean, color, etc).
  • Any other type (like a string) as long as you supply the values. See this example:
protected override void OnDisplayedValuesNeeded(DisplayedValuesNeededEventArgs e)
{
    if (e.PropertyEnum.Property.Id == 1000)
    {
        e.DisplayedValues = new string[] { "Mercury", "Venus", "Earth", "Mars",
            "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto" };
    }
    base.OnDisplayedValuesNeeded(e);
}
# Custom control for the listbox

It is possible to customize the content of the listbox using a control implementing the IDropDownContent interface.

An example in the sample code edits an alpha color. This involves using the PropertyDropDownContentAttribute class on a property or as an argument of methods like AppendProperty. You can register your custom control so that it is automatically selected for a particular Property type:

RegisterDropDownContent(typeof(Enum), typeof(DropDownContentListBox));

# FeelEditUnit

This creates an inplace control with a textbox for a main value and a combobox for an attached secondary value. The feel is called like this since it is mainly use to display a numerical value and its unit. This feel must be used in conjunction with the PropertyUnitLook and a second value must be attached to the property (see Multiple values per Property).

At design-time:

[ValueAddedToProperty(PropertyUnitLook.UnitValue, "Frequency")]
public Units Unit { set; get; }

[PropertyLook(typeof(PropertyUnitLook))]
[PropertyFeel(PropertyGrid.FeelEditUnit)]
public int Frequency {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Frequency", this, "Frequency", "");
propEnum.Property.AddValue(PropertyUnitLook.UnitValue, this, "Unit", null);
propEnum.Property.Look = new PropertyUnitLook();
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelEditUnit);

Good to know

When AddValue() is used, the target instance can be different than the one specified for the main property.

# FeelButton and FeelEditButton

This creates an inplace control with a button to the right of the string value and an optional textbox.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelButton)]
public string MyProperty {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Label", this, "MyProperty", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelButton);

# > Setup

At both design-time and runtime, you can extend the button's possible appearance by configuring it the moment it appears. This is done when handling the InPlaceCtrlVisibleEvent event, by casting the inplace control to the correct type and calling some properties:

protected override void OnInPlaceCtrlVisible(InPlaceCtrlVisibleEventArgs e)
{
    if (e.PropertyEnum.Property.Id == myId)
        ((PropInPlaceButton)e.InPlaceCtrl).ButtonText = "Add...";

    base.OnInPlaceCtrlVisible(e);
}
# Accessible properties include:
Property Description
ButtonText Sets the caption of the button.
FullWidthButton Indicates if the button should be displayed on the whole value column. If this is the case, the text of the button is the string value of the property.

These properties can also be setup by using an attribute at design-time. See the description of the ButtonSettingsAttribute class.

# FeelFontButton

This creates an inplace control showing a button at the right of the string value plus an optional textbox.

When the button is clicked, a Font dialog editor opens.

# > Configuring a Property

At design-time:

public Font MyFont {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Font", this, "MyFont", "");

As you can see in both situations, no feel is explicitly attached. This is because the library automatically registers some feels (and some looks) with types.

# FeelDateTime

This creates a DateTimePicker control, supporting editing of both date and time. It supports only an updown button, no dropdown month calendar.

MSPG offers only a textbox equipped with a month calendar control to edit a DateTime property. SPG, however, supplies a true DateTimePicker control that enables editing a time or a date field by field with an updown button. When using this feel, you must also provide a PropertyDateTimeLook (see SPG built-in Look classes).

# > Configuring a Property

At design-time:

[PropertyLook(typeof(PropertyDateTimeLook), DateTimePickerFormat.Short)]
[PropertyFeel(PropertyGrid.FeelDateTime)]
public DateTime DateTime
{

At runtime:

propEnum = AppendProperty(parentEnum, id, "Date", this, "MyDate", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelDateTime);
propEnum.Property.Look = new PropertyDateTimeLook(DateTimePickerFormat.Short);

# > Setup

You can set the usual properties for DateTimePicker controls using the look class. At runtime this is trivial:

(propEnum.Property.Look as PropertyDateTimeLook).CustomFormat = "HH:mm";
At design time, you need to explicitly assign the look:
[PropertyLook(typeof(PropertyDateTimeLook), "HH:mm")]
public DateTime DateTime {

Note

The DateTimePicker control from Microsoft does not accept null values. Therefore, it is impossible to use this feel when using a nullable DateTime?, or when using SelectedObjects.

# FeelCheckbox

This creates an inplace control showing one or more checkboxes.

This must be used in conjunction with the PropertyCheckboxLook class.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelCheckbox)]
[PropertyLook(typeof(PropertyCheckboxLook))]
public bool MyProperty {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Label", this, "MyProperty", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid. FeelCheckbox);
propEnum.Property.Look = new PropertyCheckboxLook();

# > Property types

The checkbox inplace control can be used for any Property type that has a set of different possible values (like enumeration, boolean, color, etc).

Note

Only one checkbox is shown for booleans and for a type publishing only 2 possible values.

Enumerations must be marked with the Flags attribute.

# Example

This sets the possible value for two different properties of the same type at design-time:

[Flags]
public enum Countries
{
    NoCountry = 0,
    Country1 = 1,
    Country2 = 2,
    AllCountries = 3
}

private Countries _countries = Countries.NoCountry;

[PropertyValueDisplayedAs(new string[] { "None", "France", "Spain", "All" })]
public Countries Countries_set1 {
...
[PropertyValueDisplayedAs(new string[] { "None", "Germany", "Ireland", "All" })]
public Countries Countries_set2 {
...

# > Dynamic sets of values

This feel can also handle dynamic sets of values. Possible values must be supplied in a collection. Selected values must be stored in an ArrayList, since more than once item may be selected:

private ArrayList _projects = new ArrayList();
public ArrayList Projects {
    ...
List<string> stdValues = new List<string>();
stdValues.Add("Windows 2000");
stdValues.Add("Windows XP");
stdValues.Add("Windows Server 2003");
stdValues.Add("Windows Vista");

propEnum = AppendProperty(subEnum, _id++, "Projects", this, "Projects", "",
    new PropertyValueDisplayedAsAttribute(stdValues));
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelCheckbox);
propEnum.Property.Value.Look = new PropertyCheckboxLook();

# > Setup

The PropertyGrid has a global setting that configures how to change the value of a checkbox when it is clicked by the user. A click can be effective on the checkbox only, on the checkbox and the value string or on the complete row.

PropertyGrid.CheckboxAndRadioButtonClickMode =
      CheckboxAndRadioButtonClickModes.ButtonAndString;

# FeelRadioButton

This creates an inplace control showing multiple radiobuttons.

This feel must be used in conjunction with the PropertyRadioButtonLook class.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelRadioButton)]
[PropertyLook(typeof(PropertyRadioButtonLook))]
public string MyProperty {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Label", this, "MyProperty", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid. FeelRadioButton);
propEnum.Property.Look = new PropertyRadioButtonLook();

# > Property types

The radiobutton inplace control can be used for any Property type that has a set of different possible values (like enumeration, boolean, color, etc).

The examples given for customizing the possible values of checkbox and listbox feels also apply to the RadioButton feel.

# > Setup

The PropertyGrid has a global setting that configures how to change the value of checkboxes when it is clicked by the user. A click can be effective on the checkbox only, on the checkbox and the value string or on the complete row.

PropInPlaceCheckbox.CheckboxAndRadioButtonClickMode =
      CheckboxAndRadioButtonClickModes.CheckboxAndString;

# FeelTrackbar and FeelTrackbarEdit

This creates a trackbar to the right of an optional textbox.

# > Configuring a Property

At design-time:

[PropertyFeel(PropertyGrid.FeelTrackbar)]
public int MyProperty {

At runtime:

propEnum = AppendProperty(parentEnum, id, "Label", this, "MyProperty", "");
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelTrackbar);

# > Property types

The inplace control uses a custom generic trackbar and therefore can be attached to integer, float, double and decimal property types.

# > Setup

At both design-time and runtime, you can extend the trackbar's behaviour by configuring it the moment it appears. This is done when handling the InPlaceCtrlVisibleEvent event, by casting the inplace control to the correct type and calling some properties:

protected override void OnInPlaceCtrlVisible(InPlaceCtrlVisibleEventArgs e)
{
    if (e.PropertyEnum.Property.Id == myId)
        ((PropInPlaceTrackbar)e.InPlaceCtrl).SmallChange = 2;

    base.OnInPlaceCtrlVisible(e);
}
# Some available properties
Property Description
SmallChange Accesses the SmallChange property of the .Net trackbar.
LargeChange Accesses the LargeChange property of the .Net trackbar.
Resolution Must be set for non integer property types (for integers it is equal to 1). It determines the smallest step the trackbar can accept.
RealtimeChange If true, modifying the trackbar immediately changes the value of the Property.
If false, the value is validated only on leaving the control.
TrackBar Returns a reference to the actual .Net trackbar control used internally.
LargeChange Allows to switch the effect of Up/Down and PageUp/PageDown keys for those who find the .Net trackbar has a design flaw.

The three first properties (SmallChange, LargeChange and Resolution) can also be setup by using an attribute at design-time. See the description of the TrackBarSettingsAttribute class.

# Using a validator class to set limits

A validator class must be used to set limits on the trackbar. SPG comes with a handy min/max validator for just this purpose. Assign it to the property and the feel will detect it:

[PropertyValidator(typeof(PropertyValidatorMinMax), 0, 200)]
public int MyProperty {

# Handling the PropertyValue’s value

# Setting the value programmatically

Typically, the value of a property is edited by the end-user. However, you may want to do this programmatically, for example when dealing with managed Properties.

# > To set a value programmatically

propEnum.Property.Value.SetValue(myValue)

myValue is a value of the property’s type or whose type can be converted to the property’s type.

# > To return the property’s value

  1. Use the following:
object value = propEnum.Property.Value.GetValue();
  1. Cast it to the expected type.

# Multiple value updates

Sometimes you need to update many values at the same time. This can cause the PropertyGrid to flicker because the control is refreshed by each update. You can prevent this by encapsulating all your updates in the following block:

myGrid.BeginUpdate();
...
myGrid.EndUpdate();

# Resetting to a default value

If a property has a DefaultValue attribute applied to it or if the target instance defines a Reset method for this property, you can restore a default value by calling the PropertyValue.ResetToDefaultValue method.

# Testing for multiple values

When you use multiple target instances on a Property (by using SelectedObjects or by calling PropertyGrid.AddTargetInstance) non-unique values may result in the Property displaying a blank string.

# > To test for multiple values

bool valueUnicity = propEnum.Property.Value.HasMultipleValues;

# Helper properties

There are several helper properties in the PropertyValue class:

Helper property Returns
TargetInstance(s) Target instance(s) attached to a Property.
PropertyDescriptor(s) PropertyDescriptor(s) attached to a Property.
TypeConverter Registered TypeConverter for the type of the Property’s value.

# Handling the PropertyValue’s displayed values

A Property may have a set of possible displayed values. In the .Net framework, this is done by implementing the GetStandardValues method in a TypeConverter. Of course, SPG reads these values and it also provides many ways to alter the corresponding strings that are displayed. It can even create from scratch a set of displayed values which will save you time by avoiding to write a TypeConverter.

There are three ways to supply displayed values:

  1. Through the DisplayedValuesNeeded event which is generated when a Property is created or when explicitly requested.
  2. By applying the PropertyValueDisplayedAs attribute to a property or its type.
  3. By calling the PropertyValue.ResetDisplayedValues method.

The displayed values are passed to SPG in various ways that are the same for these three methods. Therefore let’s see how it works with ResetDisplayedValues only.

# Passing strings

If standard values exist and you know exactly in which order they are published, then you can assign new strings for these values. Here is how to do it for a boolean:

propValue.ResetDisplayedValues(new string[] { "Enabled", "Disabled" });

If the boolean property must be displayed as a checkbox without strings, you can use:

propValue.ResetDisplayedValues(new string[] { "", "" });

You must ensure that the number of strings in the passed array is the same than the number of published displayed values.

# Passing objects

If a property has a type without standard values, passing the standard values as objects will create a mapping between these objects and their string representation returned by the type’s TypeConverter ConvertToString method (which is a call to ToString by default). Here is an example taken from the supplied sample:

propValue.ResetDisplayedValues(false, new object[] {
                    new Planet("Mercury"),
                    new Planet("Venus"),
                    new Planet("Earth"),
                    new Planet("Mars"),
                    new Planet("Jupiter"),
                    new Planet("Saturn") });

Typing “Mars” in the textbox will assign Planet(“Mars”) to your property as long as the ToString method for your Planet class returns “Mars”.

# Passing pairs

Now let’s say that your planets are not constructed with a name (instead it takes an ordinal for a position in the solar system) and that this name is not even part of the planet properties. There won’t be a ToString method and we need a way to specify a mapping between the planets and their string representation. This is done by using pairs, like this:

propValue.ResetDisplayedValues(true, new object[] {
                    new Planet(1), "Mercury",
                    new Planet(2), "Venus",
                    new Planet(3), "Earth",
                    new Planet(4), "Mars",
                    new Planet(5), "Jupiter",
                    new Planet(6), "Saturn" });

# Managing standard values

You must keep in mind that if you don’t specify a string representation for a published standard value, then the regular string will be used instead. It enables your client application to pass only the strings it wants to modify.

If however you want to remove some values, you can pass null instead of a string. For example, this code will create a list of colors, except Red:

propValue.ResetDisplayedValues(true, new object[] { Color.Red, null });

# Passing nothing

One of the overrides for PropertyValue.ResetDisplayedValues has this signature:

public void ResetDisplayedValues(PropertyGrid.ResetDisplayedValuesTriggerMode triggerMode)

As you can see, no standard values are passed to the PropertyValue. This is a way to tell SPG to clear all known possible values and to retrieve them in the usual way, i.e. through a TypeConverter, a PropertyValueDisplayedAs attribute or the DisplayedValuesNeeded event. In the case of the event, the unique parameter of the ResetDisplayedValues method indicates when to trigger it:

triggerMode Meaning
None The event won’t be triggered.
TriggerOnceNow The event will be triggered once during the execution of ResetDisplayedValues.
TriggerAtDropDown The event will be triggered each time a dropdown box is requested by the user.

The last two options can be used together.

Last Updated: 5/25/2022, 1:18:09 PM