Archives

Anticipation

  • No dates present

Combos of Enums

It’s probably about time that I continued on from my previous post. This one is just going to be a short one, but it’s a nice setup for the following post in the series.

When you have a property which can only have one of a limited number of values, there are a few different ways to represent it — both in the UI and in the code. In the UI, the choice is usually between a combo box and a list box, with which one you choose partly driven by personal taste and partly based on whether you think it might be possible to support multiple selections in the future — in general, a combo box is ideal for cases which only support a single selection, and a list box for cases with multiple selections. (These aren’t hard limitations, of course — you can have a single-select list box or a combo-box which does multi-select with a lingering drop-down or just via checkboxes in its drop-down, and sometimes you’ll want to do this for aesthetic or sizing reasons — but those are the conventions that people have come to expect.)

In the code, you can create a collection and fill it with all possible values as discrete objects; this certainly makes it nice and easy to bind to the UI, and is really the only option if all the possible values aren’t known at compile time (eg. you have to load them from a database, or you’re letting the user select between items they’ve already created in some other part of your software). If you know all the possible values at compile time, though, and there isn’t any “extra” info you need to store about a single item, then an enumeration is a good fit to represent the value in your data model.

Once you have an enumeration in the data model, though, how do you get its values into the combo box? One way of course is to “translate” it in your view-model layer, by converting it from the enum value to one of a collection of UI placeholder objects (as described above), and then back again. That seems like quite a bit of overhead, though.

Another way to do it is to get the .NET framework itself to help you out. There’s a static method you can call which returns a collection of all the possible values of an enum type (this is Enum.GetValues). You could expose this as a property in your view-model and bind to it, but you can instead get the XAML to do all the work for you:

<Window.Resources>
    <ObjectDataProvider x:Key="ReportTypes" ObjectType="{x:Type System:Enum}" MethodName="GetValues">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="app:ReportType" />
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>
    ...
    <ComboBox ItemsSource="{Binding Source={StaticResource ReportTypes}}" SelectedItem="{Binding ReportType, Mode=TwoWay}" />

(This will obtain the values in the same order in which they’re defined in the code, which is usually what you want — but you can interpose something which sorts the list as well if you like.)

If you try this for yourself, though, you’ll find that it has a bit of a hole — the enum values are presented to the user using the same name as it appears in code. Now, if your enum values are simple enough (a single word, for example, so you don’t run into BumpyCasingIssues), or if this is for a prototype or otherwise cheap and hacky program, then this might be ok, but for most real-world usage it leaves a bit to be desired.

<Window.Resources>
    <app:ReportTypesConverter x:Key="ReportTypesConverter" />
    <DataTemplate DataType="{x:Type app:ReportTypes}">
        <TextBlock Text="{Binding Converter={StaticResource ReportTypesConverter}}" />
    </DataTemplate>
</Window.Resources>

The standard way of changing the appearance of an item in WPF is to template it, and this is no exception. By adding the code above, the enum values will automatically be displayed in the UI using the text returned by the ReportTypesConverter, which is an IValueConverter you’ll have to write yourself that converts from an enum value to the text you want to display for it in the UI. (Note that you don’t have to write a conversion back the other way.) You can use similar techniques if you wanted to do something else with your enums — for example, displaying an image in addition to or instead of the text.

[Conversion(typeof(ReportTypes), typeof(string))]
public class ReportTypesConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        switch ((ReportTypes)value)
        {
            case ReportTypes.Basic: return "Basic Report";
            case ReportTypes.Extended: return "Enhanced Report (includes section B)";
            ...
        }
        return "Unknown (" + value + ")";
    }
 
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

You can get quite fancy in this converter (eg. handling translation) — and there’s even a technique you can use to let you use a Description attribute on the enum values themselves to define the text you want to show in the UI (and a further technique to extend that to be translatable); which at least means you’d only need to write one converter which would work for all enum types. But I’m not going to go into that at the moment. (I can post it if there’s sufficient interest, but I don’t really like that solution as it mixes too much UI-view stuff into the data-model.)

There’s yet another way to do it, though, which I think is a bit more elegant — but that will have to wait until the next post in the series.

2 comments to Combos of Enums


  • Catchable fatal error: Object of class WP_Comment could not be converted to string in /home/uecasm/public_html/lambert/wpdata/wp-content/themes/atahualpa/functions/bfa_custom_comments.php on line 16