Derorrist
Derorrist

Reputation: 3013

ComboBox binding works only one way with Color property

I'm implementing an ObservableCollection <-> DataGrid binding. My class consists of a System.Windows.Media.Color field (named 'Color') and several strings.

My implementation in WPF of the Color column:

<DataGridTemplateColumn Header="Color" Width="100">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox ItemsSource="{Binding Source={StaticResource ColorProperties}}" 
                      SelectedItem="{Binding Path=Color, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" 
                      Style="{StaticResource ComboBoxFlatStyle}">
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="1" Orientation="Horizontal">
                            <Rectangle Fill="{Binding}" Height="10" Width="10" Margin="2"/>
                            <TextBlock Text="{Binding}" Margin="2,0,0,0"/>
                        </StackPanel>
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

And ColorProperties:

<ObjectDataProvider x:Key="ColorProperties" ObjectType="{x:Type color:ColorHelper}" 
                    MethodName="GetColorNames"/>

The ColorHelper class (I'll try to find the OP for credit):

public static class ColorHelper
{
    public static IEnumerable<string> GetColorNames()
    {
        foreach (PropertyInfo p
          in typeof(Colors).GetProperties(
          BindingFlags.Public | BindingFlags.Static))
        {
            yield return p.Name;
        }
    }
}

This works just fine, I can see the combo-box with all the colors:

enter image description here

Now I added a few objects to the ObservableCollection, and their respective fields are populated in the DataGrid. All except the Color.

The default Color is Black, but when I run the application, the ComboBox stays empty.

When I select a color from the ComboBox, the ObservableCollection changes accordingly. However if I change the object itself, the ComboBox stays with its original value.

For troubleshooting, I added

<DataGridTextColumn Header="Color" Binding="{Binding Color, UpdateSourceTrigger=LostFocus}" 
                    Width="100"/> 

This column gets populated with the string representation of the color, and when I change the value from either side, it changes accordingly. This fact suggests that there's something missing in the ComboBox implementation.

Am I missing some sort of translation method? Seems weird since it actually works in one way (from ComboBox to object).

I've searched everywhere for a solution, without success.

EDIT: I implemented Color<->String converter, still doesn't work..

public class StringToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Color content = (Color)value;

        return content.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string content = (string)value;

        return (Color)ColorConverter.ConvertFromString(content);
    }
}

WPF change:

<ComboBox ItemsSource="{Binding Source={StaticResource ColorProperties}}" 
                                              SelectedItem="{Binding Path=Color, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, 
                                                             Converter={StaticResource StringToColorConverter}}"
                                              Style="{StaticResource ComboBoxFlatStyle}">

Any ideas?

Thanks!

Upvotes: 2

Views: 540

Answers (2)

Derorrist
Derorrist

Reputation: 3013

OK i found out what was wrong. It looks like getting the actual name from a color is not that simple. ToString returns the hex string, and not the actual color name. So I used the answer from http://www.blogs.intuidev.com/post/2010/02/05/ColorHelper.aspx to create the converter.

public class ColorToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Color KnownColor;

        Color content = (Color)value;

        Type ColorType = typeof(System.Windows.Media.Colors);
        PropertyInfo[] ColorsCollection = ColorType.GetProperties(BindingFlags.Public | BindingFlags.Static);

        foreach (PropertyInfo pi in ColorsCollection)
        {
            KnownColor = (Color)pi.GetValue(null);
            if (KnownColor == content)
                return pi.Name;
        }

        return String.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string content = (string)value;

        return (Color)ColorConverter.ConvertFromString(content);
    }
}

Upvotes: 0

Muds
Muds

Reputation: 4116

The list bound to Combobox is a list of strings, whereas the actual object is not just a string. you need a converter that converts from string to your desired object.

Rest all seems fine, system dosent know how does string Black translates to Color Black

-- one way binding works because, color object knows how to get color from a string, that is why when we set Background="String" it get the corresponding object using FromName

Upvotes: 1

Related Questions