Sameer Singh
Sameer Singh

Reputation: 1366

WinForms ComboBox DataBound to Enum - Format String

This is just an investigation into the best way to format string values on a ComboBox originating from an Enum.

I know I can databind a ComboBox to an Enum like so:

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }

    protected override void OnLoad(EventArgs e)
    {
        comboBoxNames.DataSource = Enum.GetValues(typeof (Names));
    }
}

public enum Names
{
    JohnDoe,
    JaneDoe,
    JohnJackson,
    JackJohnson
}

This is the result:

enter image description here

I would like to format the display values of the ComboBox such that there is a space between the PascalCase string values, while also passing through the Enum value of the selected item which I can use in a switch statement later on:

switch ((Names)comboBoxNames.SelectedItem)
{
    case Names.JohnDoe:
        // Do something John Doe-specific
        break;
    case Names.JaneDoe:
        // Do something Jane Doe-specific
        break;
    case Names.JohnJackson:
        // Do something John Jackson-specific
        break;
    case Names.JackJohnson:
        // Do something Jack Johnson-specific
        break;
}

I know that there is a ComboBox.FormatString property but I am unsure about how to use it to format the Names enum the way that I want.

Is this possible? I would prefer not to use attributes on the Enum because using that ultimately involves using reflection which seems like overkill for something that seems so simple. Any help would be much appreciated.

Upvotes: 1

Views: 966

Answers (2)

Sameer Singh
Sameer Singh

Reputation: 1366

I have figured out a way to format string values on a ComboBox originating from an Enum that I like, without requiring the ComboBox.FormatString property or the Format event: use a Dictionary<Enum, string>. Sample code below:

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }

    protected override void OnLoad(EventArgs e)
    {
        comboBoxNames.DataBindToEnum(default(Names).ToDictionary());
    }

    private void btnOK_Click(object sender, EventArgs e)
    {
        switch (((KeyValuePair<Names, string>)comboBoxNames.SelectedItem).Key)
        {
            case Names.JohnDoe:
                // Do something John Doe-specific
                MessageBox.Show(@"John Doe!");
                break;
            case Names.JaneDoe:
                // Do something Jane Doe-specific
                MessageBox.Show(@"Jane Doe!");
                break;
            case Names.JohnJackson:
                // Do something John Jackson-specific
                MessageBox.Show(@"John Jackson!");
                break;
            case Names.JackJohnson:
                // Do something Jack Johnson-specific
                MessageBox.Show(@"Jack Johnson!");
                break;
        }
    }
}

public enum Names
{
    JohnDoe,
    JaneDoe,
    JohnJackson,
    JackJohnson
}

public static class EnumExtensions
{
    public static Dictionary<T, string> ToDictionary<T>(this T enumeration)
    {
        if (!enumeration.GetType().IsEnum) return null;

        var enumValues = Enum.GetValues(typeof(T)).OfType<T>().ToList();
        return enumValues.ToDictionary(enumValue => enumValue, enumValue => enumValue.ToString().Spaceify());
    }
}

public static class StringExtensions
{
    public static string Spaceify(this string self)
    {
        for (var i = 1; i < self.Length - 1; i++)
        {
            if (char.IsLower(self[i - 1]) && char.IsUpper(self[i]) ||
                self[i - 1] != ' ' && char.IsUpper(self[i]) && char.IsLower(self[i + 1]))
            {
                self = self.Insert(i, " ");
            }
        }
        return self;
    }
}

public static class ControlExtensions
{
    public static void DataBindToEnum<T>(this ListControl listControl, IDictionary<T, string> enumAsDictionary)
    {
        listControl.DataSource = new BindingSource(enumAsDictionary, null);
        listControl.DisplayMember = "Value";
        listControl.ValueMember = "Key";
    }
}

A couple of things to note:

  • Instead of setting the ComboBox.DataSource directly as a list of the enum, set it using a Dictionary<Enum, string> and set the DisplayMember and ValueMember accordingly (extension method DataBindToEnum)
  • Currently we cannot use the enum keyword as a type restriction (something like public static Dictionary<T, string> ToDictionary<T>(this T enumeration) **where T : enum**). This is why an enum check is required in the body of that method
  • The Spaceify extension method adds spaces to PascalCase strings (handles acronyms as well)
  • The "magic" line is where I use the default keyword on the enum so that I can work on it as a list-type object and get around the issue of invoking a method on an "instance" of the enum
  • Finally, when using out the ComboBox.SelectedItem I need to cast it to a KeyValuePair<Enum, string> and pull out the Key which I can use in my switch statement. No more magic strings!

Hope this helps!

Upvotes: 0

Nicolas Tyler
Nicolas Tyler

Reputation: 10552

this is easy, just add the format event on your combobox and make it look like this:

private void comboBoxNames_Format(object sender, ListControlConvertEventArgs e)
{
    StringBuilder newText = new StringBuilder(e.Value.ToString().Length +1);
    newText.Append(e.Value.ToString()[0]);
    for (int i = 1; i < e.Value.ToString().Length; i++)
    {
        if (char.IsUpper(e.Value.ToString()[i]) && e.Value.ToString()[i - 1] != ' ')
            newText.Append(' ');
        newText.Append(e.Value.ToString()[i]);
    }
    e.Value = newText.ToString();
}

Upvotes: 1

Related Questions