5
votes

I created a ComboBox listing the colors that System.Windows.Media.Colors predefines, using the approach told in this question: How can I list colors in WPF with XAML?

My XAML code now is:

<Window ...>
    <Window.Resources>
        <ObjectDataProvider 
            ObjectInstance="{x:Type Colors}" MethodName="GetProperties" x:Key="ColorList" />
        <local:StringToBrushConverter x:Key="FontColorConversions" />
    </Window.Resources>

    <Grid Background="Black">

        ...

        <ComboBox  Grid.Column="1" Grid.Row="1" Height="22" Width="240" 
                   VerticalAlignment="Center" HorizontalAlignment="Left"
                   ItemsSource="{Binding Source={StaticResource ColorList}}"
                   SelectedValue="{Binding FontColor, Mode=TwoWay}"
                   DisplayMemberPath="Name"
                   SelectedValuePath="Name">
            <ComboBox.ItemContainerStyle>
                <Style TargetType="ComboBoxItem">
                    <Setter Property="Foreground" Value="{Binding Converter={StaticResource FontColorConversions}}"/>
                </Style>
            </ComboBox.ItemContainerStyle>
        </ComboBox>
        ...
    </Grid>
</Window>

And besides, please note that I bind SelectedValue to a VM class's FontColor property, which is of string type.

class FontSetting : INotifyPropertyChanged
{
    private string _fontColor = "Lavender";   // initial color
    public event PropertyChangedEventHandler PropertyChanged;

    public string FontColor
    {
        get
        {
            return _fontColor;
        }
        set
        {
            _fontColor = value;
            OnPropertyChanged("FontColor");
        }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

And I set the DataContext of the Window containing this ComboBox to an instance of FontSetting.

So each item in the ComboBox actually display a string representing a certain color now, what I want to do is set an item's Foreground color to that color its content indicates, like this:

enter image description here

Can anyone help? Thanks.

UPDATED: Since most of the solutions have a converter which converts string to Brush and actually I already have it, now I want to put mine here, as I binded a TextBox's Foreground to FontSetting's FontColor property, so that when you change the ComboBox, the color of that TextBox changes accordingly.

Here is my converter class, and it works fine by now:

   class StringToBrushConverter : IValueConverter
    {
        public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            BrushConverter conv = new BrushConverter();
            SolidColorBrush brush = conv.ConvertFromString("Lavender") as SolidColorBrush;
            if (null != value)
            {
                brush = conv.ConvertFromString(value.ToString()) as SolidColorBrush;
            }
            return brush;
        }

        public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }

When I click the ComboBox to open the dropdown list, I got an exception:

enter image description here

CONCLUSION

Amine's solution works, it's my mistake. I explain briefly now, if you bind a ComboBox to System.Windows.Media.Colors like what I am doing, when the item is rendered, the Convert() method of the converter class (which you assign to the binding) is executed, and actually the value passed to Convert() as its first parameter is a Syetem.Windows.Media.Color instance. I made mistake coz I thought it was of string type.

Therefore, in my case I need two converter classes, one converting string to Brush, and the other one converting Color to Brush. So I will keep my own StringToBrush converter and add Amine's ColorToBrush converter.

However, I simplified Amine's implementation a bit:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    BrushConverter conv = new BrushConverter();
    SolidColorBrush brush = SolidColorBrush)conv.ConvertFromString(FontSetting.DEFAULT_FONT_COLOR);
    if (null != value)
    {
        PropertyInfo pi = value as PropertyInfo;
        if (null != pi)
        {
            brush = conv.ConvertFromString(pi.Name) as SolidColorBrush;
        }
    }
    return brush;
}

Moreover, Joe's input is also valuable, put all them together, I can keep the items' color consistent, which is perfect.

2

2 Answers

4
votes

You can set Style of ComboBoxItem as bellow :

    <ComboBox  Grid.Column="1" Grid.Row="1" Height="22" Width="240" x:Name="CB"
               VerticalAlignment="Center" HorizontalAlignment="Left"
               ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
               DisplayMemberPath="Name"
               SelectedValuePath="Name">
        <ComboBox.ItemContainerStyle>
            <Style TargetType="ComboBoxItem">
                <Setter Property="Foreground" Value="{Binding Converter={StaticResource converter}}"/>
            </Style>
        </ComboBox.ItemContainerStyle>
    </ComboBox>

By using this converter:

public class MyConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        object obj = ((System.Reflection.PropertyInfo)value).GetValue(this,null);           
        return (SolidColorBrush)new BrushConverter().ConvertFromString(obj.ToString());
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        return value;
    }
}

enter image description here

1
votes

Two ways, use a value converter or an intermediate property. The easiest is probably an intermediate property as you are using well structured bindings already for your SelectedItem (but value converters are fun too!).

SelectedValue is bound to FontColor, so in your setter set another value:

public string FontColor
{
    get
    {
        return _fontColor;
    }
    set
    {
        _fontColor = value;
        ForegroundColorToDisplay = GetBrushFromColorString(value);
        OnPropertyChanged("FontColor");
    }
}

public Brush _foregroundColorToDisplay
public Brush ForegroundColorToDisplay
{
    get
    {
        return _foregroundColorToDisplay;
    }
    set
    {
        _foregroundColorToDisplay= value;
        OnPropertyChanged("ForegroundColorToDisplay");
    }
}

or, if you don't want to store it:

public string FontColor
{
    get
    {
        return _fontColor;
    }
    set
    {
        _fontColor = value;
        //note it fires two changed events!
        OnPropertyChanged("ForegroundColorToDisplay");
        OnPropertyChanged("FontColor");
    }
}

public Brush ForegroundColorToDisplay
{
    get
    {
        return GetBrushFromColorString(value);;
    }
}

You can bind to this new property in your xaml:

<ComboBox  Grid.Column="1" Grid.Row="1" Height="22" Width="240" 
               VerticalAlignment="Center" HorizontalAlignment="Left"
               ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
               SelectedValue="{Binding FontColor, Mode=TwoWay}"
               DisplayMemberPath="Name"
               SelectedValuePath="Name"
               Foreground="{Binding ForegroundColorToDisplay, Mode=OneWay}"/>

If you are interested, a value converter would work like this:

Create a class in your code behind (or elsewhere if it's going to be used a lot) that implements IValueConverter, takes the string and returns a Brush:

public class StringToBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        return GetBrushFromString(value as string)
    }

    public object ConvertBack(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        Throw new SomeException();
    }
}

and use it in your binding in xaml to convert the selectedItem binding to Foreground brush:

<Window.Resources>
    <local:StringToBrushConverter  x:Key="converter" />
</Window.Resources>
...
<ComboBox  Grid.Column="1" Grid.Row="1" Height="22" Width="240" 
               VerticalAlignment="Center" HorizontalAlignment="Left"
               ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
               SelectedValue="{Binding FontColor, Mode=TwoWay}"
               DisplayMemberPath="Name"
               SelectedValuePath="Name"
               Foreground="{Binding FontColor, Mode=OneWay, Converter={StaticResource converter}}"/>