Skip to content

Value Editors

Gary edited this page Aug 27, 2014 · 1 revision

Table of Contents

Every property descriptor has an explicit or implicit value editor to allow a user to edit the property value described by the descriptor. The PropertyDescriptor.GetEditor() method returns the editor for a given type. If the property descriptor class derived from PropertyDescriptor does not implement GetEditor(), the base PropertyDescriptor.GetEditor() method is called:

public override object GetEditor(Type editorBaseType)
{
    if (m_editor != null &&
        editorBaseType.IsAssignableFrom(m_editor.GetType()))
    {
        return m_editor;
    }

    return base.GetEditor(editorBaseType);
}

If a value editor for the type editorBaseType is not found, the base method System.ComponentModel.PropertyDescriptor.GetEditor() is called, which returns a default editor for that type, assuming one can be found.

Note that the value editor is not a control itself. Instead, each value editor has a value editing control associated with it, described in Value Editors and Value Editing Controls. This value editing control class is not necessarily in the value editor class's file.

There are two types of value editors: those that derive from UITypeEditor and those that don't. They can both implement the IPropertyEditor interface, which obtains a value editing control object.

UITypeEditor Class

System.Drawing.Design.UITypeEditor is a base class for a value editor with a value editing control that either drops down, such as a color swatch, or displays a modal dialog, such as a file selection dialog. It may also display no interactive control at all, although this option is not used in ATF. The property editor for this property typically shows either a tab or an ellipsis button to display either the drop down control or dialog. The illustration below, for instance, shows the ColorEditor drop down value editing control used in the ATF Timeline Editor Sample.

Implementing a UITypeEditor

UITypeEditor includes the following methods:

  • GetEditStyle(): Get the editor style used by EditValue(), a System.Drawing.Design.UITypeEditorEditStyle indicating the displayed control's style: Modal, DropDown, or None.
  • EditValue(): Edit the value of the specified object using an appropriate value editing control. This displays the a value editing control or modal dialog and returns the value after user selection.
  • PaintValue(): Paint a representation of the value of the specified object.
Most deriving classes should implement the first two methods.

The following sections demonstrate several UITypeEditor implementations.

ColorEditor Value Editor Class

The ATF Timeline Editor Sample's "timelineRef" type has a color attribute with a color value editor defined in an XML Schema annotation:

<scea.dom.editors.attribute name="color" displayName="Color" description="Display Color"
  editor="Sce.Atf.Controls.PropertyEditing.ColorEditor,Atf.Gui"
  converter="Sce.Atf.Controls.PropertyEditing.IntColorConverter" />

This value editing control for ColorEditor is seen in the earlier illustration.

The Sce.Atf.Controls.PropertyEditing.ColorEditor.EditValue() implementation is:

public override object EditValue(
    ITypeDescriptorContext context,
    IServiceProvider provider,
    object value)
{
    TypeConverter converter = context.PropertyDescriptor.Converter;
    if (value == null)
        value = Color.Transparent;
    Color oldColor = (Color)converter.ConvertTo(value, typeof(Color)); // convert from underlying type to Color
    Color newColor = (Color)base.EditValue(context, provider, oldColor);

    return converter.ConvertFrom(newColor);
}

Note that this method gets the property descriptor (context.PropertyDescriptor) from the context and then gets a value converter from the descriptor's Converter property. It uses the converter to convert the old value to the Color Type with the ConvertTo() method.

ATF's ColorEditor derives from System.Drawing.Design.ColorEditor, so the following line invokes the base class's EditValue() method, which displays its own color picker:

Color newColor = (Color)base.EditValue(context, provider, oldColor);

This returned value is then converted to an appropriate form with the ConvertFrom() method, and that converted value is returned by EditValue().

Here is Sce.Atf.Controls.PropertyEditing.ColorEditor.PaintValue():

public override void PaintValue(PaintValueEventArgs e)
{
    if (e.Value is Color)
    {
        base.PaintValue(e);
    }
    else
    {
        TypeConverter converter = e.Context.PropertyDescriptor.Converter;
        object value = converter.ConvertTo(e.Value, typeof(Color)); // convert from underlying type to Color
        if (value is Color)
        {
            Color color = (Color)value;
            base.PaintValue(new PaintValueEventArgs(e.Context, color, e.Graphics, e.Bounds));
        }
    }
}

This method uses the value converter to convert the new color to a Color object, and then paints the new value using the base class's PaintValue() method. For information on converters, see Value converters.

BoundedIntEditor Value Editor Class

A PaintValue() method is not needed if the control handles the painting. For instance, the BoundedIntEditor value editor class implements just this EditValue() method:

public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
    IWindowsFormsEditorService editorService =
        provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
    if (editorService != null)
    {
        if (!(value is int))
            value = m_min;

        IntInputControl intInputControl = new IntInputControl((int)value, m_min, m_max);
        editorService.DropDownControl(intInputControl);
        // be careful to return the same object if the value didn't change
        if (!intInputControl.Value.Equals(value))
            value = intInputControl.Value;
    }

    return value;
}

This EditValue() method creates an IntInputControl control (a separate class) that handles all control painting. Similarly, EnumUITypeEditor provides a ListBox control to list enumerated values and does not implement PaintValue.

BoundedFloatEditor Value Editor Class

BoundedFloatEditor creates a dropdown value editing control with a slider and textbox to enter a floating point number.

It implements GetEditStyle() to specify how the control appears, dropdown in this case:

public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
    return UITypeEditorEditStyle.DropDown;
}

Its EditValue() method gets a IWindowsFormsEditorService object to display a control in a drop-down area and uses it to display a FloatInputControl value editing control it creates and drops down with IWindowsFormsEditorService.DropDownControl():

public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
    IWindowsFormsEditorService editorService =
        provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
    if (editorService != null)
    {
        if (!(value is float))
            value = m_min;

        FloatInputControl floatInputControl = new FloatInputControl((float)value, m_min, m_max);
        editorService.DropDownControl(floatInputControl);
        // be careful to return the same object if the value didn't change
        if (!floatInputControl.Value.Equals(value))
            value = floatInputControl.Value;
    }

    return value;
}

FolderBrowserDialogUITypeEditor Value Editor Class

This value editor displays a folder selection dialog, and its constructor creates this FolderBrowserDialog for later use:

public FolderBrowserDialogUITypeEditor(string description)
{
    // create instance of editing control,
    // resuse this instance for subsequence calls
    m_dialog = new FolderBrowserDialog();
    m_dialog.Description = description;
}

Its GetEditStyle() method indicates a modal dialog is displayed:

public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
    return UITypeEditorEditStyle.Modal; // editor type is modal dialog.
}

The EditValue() method displays the FolderBrowserDialog, gets the user's selected path from it, and returns this value:

public override object EditValue(ITypeDescriptorContext context, IServiceProvider sp, object value)
{
    IWindowsFormsEditorService editorService =
        (IWindowsFormsEditorService)sp.GetService(typeof(IWindowsFormsEditorService));

    if (editorService != null)
    {
        if (value != null)
            m_dialog.SelectedPath = value.ToString();
        m_dialog.ShowDialog();
        value = m_dialog.SelectedPath;
    }

    return value;
}

IPropertyEditor Interface

IPropertyEditor contains this method to get the value editing control associated with the value editor:

Control GetEditingControl(PropertyEditorControlContext context);

For example, the BoundedIntEditor value editor class implements IPropertyEditor:

public virtual Control GetEditingControl(PropertyEditorControlContext context)
{
    return new BoundedIntControl(context, m_min, m_max);
}

As noted previously, UITypeEditor classes, of which BoundedIntEditor is one, can also implement IPropertyEditor but are not required to do so, because the UITypeEditor.EditValue() method instantiates the value editing control. If a UITypeEditor does implement IPropertyEditor, it does not need to return the same control that it uses in UITypeEditor.EditValue(), and these controls are indeed different for BoundedIntEditor. As seen previously, BoundedIntEditor.EditValue() uses IntInputControl and BoundedIntEditor.GetEditingControl() returns a BoundedIntControl, a protected class inside BoundedIntEditor.

Property editors, containing value editors, ultimately derive from the PropertyView class. When a selection occurs, PropertyView calls PropertyDescriptor.GetEditor() to get the value editor (for a property of an item in the selection) and determines if it implements IPropertyEditor. If it does, IPropertyEditor.GetEditingControl() is called to get the value editing control, which is used to edit the value. Otherwise, UITypeEditor.EditValue() gets called to edit the value. Thus the IPropertyEditor value editing control has priority and would be used — in ATF. However, a UITypeEditor can be used with a .NET Framework two-column style property editor, because UITypeEditor is a .NET Framework class. In this case, the control specified in the UITypeEditor.EditValue() method would be used.

Non UITypeEditor Value Editors

If a value editor does not derive from UITypeEditor, it must implement IPropertyEditor to specify the value editing control.

Implementing a Non UITypeEditor

Such editors need to implement IPropertyEditor.GetEditingControl(), which is demonstrated here with BoolEditor.

BoolEditor Value Editor Class

BoolEditor is used in several samples, including the ATF Simple DOM Editor Sample. This figure shows the property editor for an "Animation" object:

The "Compressed" Boolean property is shown with the check box associated with the BoolEditor.

Here is IPropertyEditor.GetEditingControl() for BoolEditor:

public Control GetEditingControl(PropertyEditorControlContext context)
{
    m_boolControl = new BoolControl(context);
    return m_boolControl;
}

BoolEditor also contains the implementation of the private BoolControl class, although it could be in a separate file.

BoolControl's constructor creates a CheckBox control object, which is shown in the previous illustration of the value editor's control:

public BoolControl(PropertyEditorControlContext context)
{
    m_context = context;

    m_checkBox = new CheckBox();
    m_checkBox.Size = m_checkBox.PreferredSize;
    m_checkBox.CheckAlign = ContentAlignment.MiddleLeft;
    m_checkBox.CheckedChanged += checkBox_CheckedChanged;

    Controls.Add(m_checkBox);
    Height = m_checkBox.Height + m_topAndLeftMargin;

    RefreshValue();
}

The other methods in BoolControl do things like refresh the control, and check for changed state or size.

IAnnotatedParams Interface

If a value editor can have parameters in an XML Schema annotation, the value editor must implement IAnnotatedParams. This interface gets the parameters from the schema annotation and initializes the value editor for those parameters.

For example, the ATF Timeline Editor Sample defines a value editor for a file name that takes a file filter parameter in its schema definition for the "timelineRef" type:

<scea.dom.editors.attribute name="ref" displayName="File Name" description="File name of timeline reference"
  editor="Sce.Atf.Controls.PropertyEditing.FileUriEditor,Atf.Gui.WinForms:Timeline files (*.timeline)|*.timeline"/>

The editor FileUriEditor implements IAnnotatedParams:Initialize() to obtain the filter string and place it in the m_filter field, which is used later for a file dialog:

public void Initialize(string[] parameters)
{
    if (parameters.Length >= 1)
        m_filter = parameters[0];
}

Value converters also use the IAnnotatedParams interface, as described in IAnnotatedParams Interface.

Topics in this section

Clone this wiki locally