# 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
- Property attribute, PropertyFeelAttribute.
- Parent Property attribute, PropertyFeelAttribute specifying feel of child.
- Property attribute, PasswordPropertyTextAttribute.
- Feel registered for the underlying Property type.
- UITypeEditor for the underlying Property type.
- Set of standard values published by the Property value’s underlying TypeConverter.
- 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
- Use the following:
object value = propEnum.Property.Value.GetValue();
- 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:
- Through the DisplayedValuesNeeded event which is generated when a Property is created or when explicitly requested.
- By applying the PropertyValueDisplayedAs attribute to a property or its type.
- 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.