views:

363

answers:

2

Hi everyone,

I need help with the following problem:

I have a class with two properties.

private byte m_selectedValue;
public byte SelectedValue
{
  get { return m_selectedValue; }
  set { m_selectedValue = value; }
}

private string[] m_possibleValues;
public string[] PossibleValues
{
  get { return m_possibleValues; }
  set { m_possibleValues = value; }
}

The PossibleValues stores the list of the selectable values. The SelectedValue contains the index of the actually selected value.

In this state the property editor shows the index of the selected value. I would like to select the value using a combobox in the property grid, the same style used with an Enum property. The combobox's list would be populated from the PossibleValues property.

With the help of this article (http://www.codeproject.com/KB/cpp/UniversalDropdownEditor.aspx) I have managed to create a custom editor that show the combobox on the property grid with the values from the PossibleValues property. I can also select the value, but still the property grid shows the index of the value instead of the value itself.

This is the modified source of the editor (original is from CodeProject):

public class EnumParamValuesEditor : UITypeEditor
{
    private IWindowsFormsEditorService edSvc;

    public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
    {
        if ((context != null) && (context.Instance != null))
            return UITypeEditorEditStyle.DropDown;
        return UITypeEditorEditStyle.None;
    }

    public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        if ((context == null) || (provider == null) || (context.Instance == null))
            return base.EditValue(provider, value);
        edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
        if (edSvc == null)
            return base.EditValue(provider, value);
        ListBox lst = new ListBox();
        PrepareListBox(lst, context);
        lst.SelectedIndex = (byte)value;
        edSvc.DropDownControl(lst);
        if (lst.SelectedItem == null)
            value = null;
        else
            value = (byte)lst.SelectedIndex;
        return value;
    }

    private void PrepareListBox(ListBox lst, ITypeDescriptorContext context)
    {
        lst.IntegralHeight = true;
        string[] coll = ((EnumTerminalParam)context.Instance).PossibleValues;
        if (lst.ItemHeight > 0)
        {
            if ((coll != null) && (lst.Height / lst.ItemHeight < coll.Length))
            {
                int adjHei = coll.Length * lst.ItemHeight;
                if (adjHei > 200)
                    adjHei = 200;
                lst.Height = adjHei;
            }
        }
        else
            lst.Height = 200;
        lst.Sorted = true;
        FillListBoxFromCollection(lst, coll);
        lst.SelectedIndexChanged += new EventHandler(lst_SelectedIndexChanged);
    }

    void lst_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (edSvc == null)
            return;
        edSvc.CloseDropDown();
    }

    public void FillListBoxFromCollection(ListBox lb, ICollection coll)
    {
        lb.BeginUpdate();
        lb.Items.Clear();
        foreach (object item in coll)
            lb.Items.Add(item);
        lb.EndUpdate();
        lb.Invalidate();
    }

}

Of course, it needs further modifications to correctly handle some situations (ex. the PossibleValues is empty).

So is it possible to show the PossibleValues[SelectedValue] instead of the SelectedValue in the property editor?

A: 

Instead of two separate properties why not tie them together within a Dictionary type. So much easier to use in this case. With your index as the key and the string[] as values. Just don't limit yourself to a byte for the index.

ChrisBD
If I change the PossibleValues from string[] to Dictionary<int,string> I still need a property to store the currently selected value. The application processes information from an external device and after processing the information is sent back. I use byte data type because this device accepts only values from 0 to 255.
starobrno
A: 

You need to attach a custom TypeConverter to your SelectedValue property and make the PossibleValues non browsable. The TypeConverter will be responsible for showing strings in the PropertyGrid instead of ints. So basically, you need to override CanConvertFrom, CanConvertTo, ConvertFrom and ConvertTo. When you want to get your custom strings, use the context argument passed to these methods and call your PossibleValues property in your target instance. That should make it. Seems you don't need any custom UITypeEditor here.

Nicolas Cadilhac
I thought that the GetStandardValuesSupported and GetStandardValues are used for showing a combobox in the property editor. Unfortunately, the GetStandardValues must return a collection with elements of the same type as the property itself, at least according to http://msdn.microsoft.com/en-us/library/ayybcxe5.aspx . How can I make the property editor to show the combobox with the ConvertFrom or ConvertTo methods?
starobrno
Yes, GetStandardValues will return your native type (byte). And this is what will trigger a combobox. ConvertFrom and ConvertTo will simply do the conversions to/from your custom strings so that the strings and not the bytes will be shown in the list.
Nicolas Cadilhac