Tuesday, 19 June 2007

String Collection Editor for Property Grid

It almost took me a day to figure out how to create a custom editor that can collect string values. Various techniques are available at forums, but it did not help me much. Here is the problem I faced followed by a simple solution that can allow you to create custom editor for any data type:

Problem Statement

As simple as that, I have created a component that has a property whose data type is List (Of String). I want user to see this property value in Property Grid of Visual Studio, and when he clicks on the edit collection button, he should be able to add strings, which I can reflect back in the property. By default, Visual Studio provides a Collection Editor. This collection editor initializes an object for the type. As value types like String do not have a constructor, this editor does not work for them. So, task is to create an editor which can act as a default editor for value type collections.

Recommended Solutions

I searched a lot and found similar solutions for this problem. You can decorate your property with an EditorAttribute. Here you can refer to StringCollectionEditor provided in System.Design in the following way:




[Editor(@"System.Windows.Forms.Design.StringCollectionEditor,
System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
typeof(System.Drawing.Design.UITypeEditor))]
public List StringValues
{
get
{
return stringValues;
}
set
{
stringValues = value;
}
}

But this did not worked out for me, as I could not get the values to persist when I entered them in the textbox. It is because StringCollectionEditor works well with string arrays or, specialized or string collections, but not so with List(Of T).



My Solution

Here is what I did and what you can do to create editor for any data type:

I created a custom form called StringCollectionForm.cs (just like the form shown above) and a custom editor class called StringCollectionEditor derived from UITypeEditor. To make that editor work, only one method called EditValue needs to be overridden. One of its parameters is an object that provides the value of collection. Initially this value is null. You can create any collection and return it as an object. Next time, you will receive the same object as parameter. So, you get complete control over the working of editor. I took values from the form’s textbox and added them to List(Of string). Next time, when I receive the object, I cast it back to List(Of string).

Only thing you need to make sure of is that object you are returning should be such that it can be cast to data type of the property. And you will receive the value of same data type.

Here is the C# code snippet for that:





public override object EditValue(ITypeDescriptorContext context,
IServiceProvider serviceprovider, object value)
{
if (serviceprovider != null)
{
mapiEditorService = serviceprovider
.GetService(typeof(IWindowsFormsEditorService)) as
IWindowsFormsEditorService;
}

if (mapiEditorService != null)
{
StringCollectionForm form = new StringCollectionForm();

// Retrieve previous values entered in list.
if (value != null)
{
List stringList = (List)value;
form.txtListValues.Text = String.Empty;
foreach (string stringValue in stringList)
{
form.txtListValues.Text += stringValue + "\r\n";
}
}

// Show Dialog.
form.ShowDialog();

if (form.DialogResult == DialogResult.OK)
{
List stringList = new List();

string[] listSeparator = new string[1];
listSeparator[0] = "\r\n";

string[] listValues = form.txtListValues.Text
.Split(listSeparator, StringSplitOptions.RemoveEmptyEntries);

// Add list values in list.
foreach (string stringValue in listValues)
{
stringList.Add(stringValue);
}

value = stringList;
}

return value;
}

return null;
}

Hope you find this stuff useful.

- Mohit