How-to: Build a custom UITypeEditor

Recently I built a CodeSmith sample UITypeEditor that allows a user to enter in custom data into a DropDownList. In the following article I’ll show you what you need to do in order to build your own UITypeEditor.

 

First you need to create a public class that will hold the data of the drop down list. In this example I named my class DropDownListProperty.

public class DropDownListProperty
{
}

Next we will need to add the properties and the constructors.

    public class DropDownListProperty
    {
        private List<string> _values = new List<string>();

        public DropDownListProperty()
        {
            SelectedItem = "None";
        }

        public DropDownListProperty(List<String> values)
        {
            if(values.Count > 0)
                SelectedItem = values[0];
            else
                SelectedItem = "None";

            Values = values;
        }

        public List<string> Values
        {
            get
            {
                if (_values == null)
                    _values = new List<String>();

                return _values;
            }
            set
            {
                if(value != null)
                    _values = value;
            }
        }

        [Browsable(false)]
        public string SelectedItem { get; set; }
    }

You'll notice that we have a public property called SelectedItem. This property will hold the initial value which will be the selected value when a user selects a choice. By default we set this to "None" in the constructor. We also set an attribute on the property Browsable(false). This tells the PropertyGrid not to display this property.

We now want to override the ToString() method to the DropDownListProperty so the dropdown displays the current selected value.

/// <summary>
/// The value that we return here will be shown in the property grid.
/// </summary>
/// <returns></returns>
public override string ToString()
{
    return SelectedItem;
}

Now it is time to implement the class that controls how my class is displayed in the property grid. We will want to create a class that inherits from UITypeEditor.

/// <summary>
/// Provides a user interface for selecting a state property.
/// </summary>
public class DropDownListPropertyEditor : UITypeEditor
{
}

Next we will add a private member variable named _service. We will need to declare this member variable because we will want to tie into an event in a little bit. Now it is time to override the EditValue method.

/// <summary>
/// Displays a list of available values for the specified component than sets the value.
/// </summary>
/// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
/// <param name="provider">A service provider object through which editing services may be obtained.</param>
/// <param name="value">An instance of the value being edited.</param>
/// <returns>The new value of the object. If the value of the object hasn't changed, this method should return the same object it was passed.</returns>
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
    if (provider != null)
    {
    // This service is in charge of popping our ListBox.
    _service = ((IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)));

        if (_service != null && value is DropDownListProperty)
        {
            var property = (DropDownListProperty) value;

            var list = new ListBox();
            list.Click += ListBox_Click;
   
            foreach (string item in property.Values)
            {
                list.Items.Add(item);
            }

            // Drop the list control.
            _service.DropDownControl(list);

            if (list.SelectedItem != null && list.SelectedIndices.Count == 1)
            {
                property.SelectedItem = list.SelectedItem.ToString();
                value =  property;
            }
        }
    }

    return value;
}

It is important not to be overwhelmed by the code above. The object value that is passed in is the DropDownListProperty class that holds our data. All we need to do is some safe type checking (value is DropDownListProperty) and then cast the value. The _service variable holds the property grid control that we are interacting with.

We create a ListBox object as that will hold our list of data (Values property from the DropDownListProperty class). It also exposes a Click event that will allow us to know when someone clicks on the drop down list. We will add an event handler ListBox_Click  to the Click event so we can close the drop down list. If we skipped this step then the list would always be shown.

The next few lines just adds all our data into the Listox and calls DropDownControl(Control).  This shows the populated ListBox control.

Finally we will set the SelectedItem to the Item that the user selected.

It is time to add the method that we wired up to the Click event.

private void ListBox_Click(object sender, EventArgs e)
{
   if(_service != null)
      _service.CloseDropDown();
}

The last peice to this puzzle is to override the GetEditStyle method and return that we want to display a DropDown UITypeEditorEditStyle

/// <summary>
/// Gets the editing style of the <see cref="EditValue"/> method.
/// </summary>
/// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
/// <returns>Returns the DropDown style, since this editor uses a drop down list.</returns>
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
    // We're using a drop down style UITypeEditor.
    return UITypeEditorEditStyle.DropDown;
}

Finally we will go back and add a Editor attribute to the DropDownListProperty class. This will tell the PropertyGrid that when this property type is loaded to use the new UITypeEditor class we created.

[Editor(typeof(DropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]

For more information please check out our Google Code repository. Attached is the source code for these two classes.


Posted Oct 13 2009, 12:00 AM by Blake Niemyjski |
windowscoding.com;
Copyright © 2008 Windows Coding
Microsoft and Microsoft logo's are trademarks of Microsoft Corporation.