views:

1333

answers:

2

I'm trying to understand DataForm as implemented in the November 2009 toolkit and I can't work out how to bind a ComboBox to an enum. Does anyone know how the DataForm does this automatically?

Background

First I created a class and an Enum, following this article and allowed the DataForm to generate the fields. The DataForm generated a TextBox for the Name string field and (what I assume is) a ComboBox for the Genres enum field.

My first aim in understanding how to customize the DataForm is to reproduce what is produced in the auto-generation. I managed to do the TextBoxes (and the DatePicker, excluded from this code) but I'm struggling to bind the ComboBox to the enum.

Here are the classes (simplified):

public class Movie
{
    public string Name { get; set; }
    public Genres Genre { get; set; }
}

public enum Genres
{
    Comedy,
    Fantasy,
    Drama,
    Thriller
}

and then in MainPage I'm doing this:

private ObservableCollection<Movie> movies = new ObservableCollection<Movie>();

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    Movie movie = new Movie() { Name = "Fred", Genre = Genres.Thriller };
    movies.Add(movie);
    myDataForm.ItemsSource = movies;
}

and in the MainPage.xaml, in the Grid:

<dataFormToolkit:DataForm x:Name="myDataForm" AutoEdit="False" AutoCommit="False"
                          Header="Foo Movie DB">
</dataFormToolkit:DataForm>

for the auto-generated stuff. When trying to generate it manually, I've instead got:

<dataFormToolkit:DataForm x:Name="myDataForm" AutoEdit="False" AutoCommit="False"
                          Header="Foo Movie DB">
    <StackPanel Orientation="Vertical">
        <dataFormToolkit:DataField>
            <TextBox Text="{Binding Name, Mode=TwoWay}"/>
        </dataFormToolkit:DataField>
        <dataFormToolkit:DataField>
            <ComboBox ItemsSource="{Binding Genres}"
                      SelectedItem="{Binding Genre, Mode=TwoWay}" />
        </dataFormToolkit:DataField>
    </StackPanel>
</dataFormToolkit:DataForm>

but the ComboBox doesn't work. There are a lot of articles covering this but it seems that much of what they propose is too much for an auto-generator to do (e.g. subclassing ComboBox to provide SelectedValue). Do you know how the tools do it for us?

+4  A: 

You can do this using IValueConverter:

XAML:

            <ComboBox Width="100" Height="30" ItemsSource="{Binding GenreSource,Converter={StaticResource ec}}"
                  SelectedItem="{Binding SelectedGenre, Mode=TwoWay, Converter={StaticResource gc}}"></ComboBox>


    public class DemoViewModel : ViewModel
{
    public DemoViewModel()
    {
    }

    public Type GenreSource
    {
        get
        {
            return typeof(Genres);
        }
    }


    private Genres _SelectedGenre;

    public Genres SelectedGenre
    {
        get { return _SelectedGenre; }
        set
        {
            _SelectedGenre = value;
            OnPropertyChanged("SelectedGrape");
        }
    }
} 

Convert from Enum to list for ComboBox:

public class EnumListConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var enumType = (Type)value;
        var names = new List<string>();
        foreach (var fieldInfo in enumType.GetFields(BindingFlags.Static | BindingFlags.Public))
        { 
            names.Add(fieldInfo.Name); 
        }
        return names;            
    }

and Convert from string back to list:

    public class GenreConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return (Views.Genres)Enum.Parse(typeof(Views.Genres), value.ToString(), false);
    }
}

You could pass the full type name to GenreConverter to make this generic for any enum

D.R. Payne
Looks like you could use your first upvote... good answer.
mkedobbs
It's a good, detailed answer but I didn't ask how I could do it, I asked how the DataForm does it, so I've not accepted it as the answer.
serialhobbyist
+2  A: 

This is the code that DataForm executes...

ComboBox comboBox = new ComboBox();
FieldInfo[] valueFieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.Static);
List<string> valueList = new List<string>();
foreach (FieldInfo valueFieldInfo in valueFieldInfos)
{
            Enum value = valueFieldInfo.GetValue(null) as Enum;
            if (value != 0)
            {
                valueList.Add(value.ToString());
            }
}
comboBox.ItemsSource = valueList;
return comboBox;
Mark