WPF - 将 ComboBox 项的前景绑定到其值

6

我创建了一个ComboBox,列出了System.Windows.Media.Colors预定义的颜色,使用了这个问题中提到的方法:如何在WPF中使用XAML列出颜色?

我的XAML代码现在是:

<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>

此外,请注意,我将SelectedValue绑定到一个VM类的FontColor属性上,该属性的类型为字符串。
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));
        }
    }
}

我将包含此ComboBox的窗口的DataContext设置为FontSetting的实例。

因此,现在ComboBox中的每个项目实际上显示代表某种颜色的字符串,我想要做的是将项目的前景色设置为其内容指示的颜色,就像这样:

enter image description here

有人能帮忙吗?谢谢。

更新: 由于大多数解决方案都有一个将字符串转换为Brush的转换器,而实际上我已经有了它,现在我想把我的代码放在这里,因为我将一个TextBox的前景绑定到FontSetting的FontColor属性上,所以当您更改ComboBox时,该TextBox的颜色会相应地更改。

这是我的转换器类,现在它运行得很好:

   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;
        }
    }

当我点击下拉列表框以打开下拉列表时,出现了异常:

enter image description here

结论

Amine的解决方案可行,是我的错误。现在我简要解释一下,如果将ComboBox绑定到System.Windows.Media.Colors,就像我所做的那样,当项目被呈现时,转换器类的Convert()方法(您分配给绑定的方法)将被执行,实际上传递给Convert()作为其第一个参数的值是Syetem.Windows.Media.Color实例。我犯了错误,因为我认为它是字符串类型。

因此,在我的情况下,我需要两个转换器类,一个将字符串转换为Brush,另一个将Color转换为Brush。因此,我将保留自己的StringToBrush转换器并添加Amine的ColorToBrush转换器。

但是,我稍微简化了Amine的实现:

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;
}

此外,Joe的意见也非常有价值,将它们汇总起来,我可以保持物品的颜色一致,这非常完美。
2个回答

5

您可以按照以下方式设置ComboBoxItem的样式:

    <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>

通过使用这个转换器:
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


谢谢,但是在你的代码中:Value="{Binding Converter={StaticResource converter}}",你有没有漏掉什么?你不需要指示绑定从哪个源获取值吗? - VincentZHANG
顺便说一句,你的代码不起作用。我相信你走在正确的轨道上,只是我们需要知道如何在那个ItemContainerStyle标签中绑定一个MenuItem的值。 - VincentZHANG
我的代码如截图所示能够正常工作。你有收到任何错误或异常吗? - Amine
Value="{Binding Converter={StaticResource converter}}"。不用指定值也可以,代码运行正常。 - Amine
谢谢。如果我使用自己的转换器实现版本,会出现“令牌无效”的异常,我已经更新了帖子,请查看详细信息。如果我使用您的转换器版本,在编译时会出现异常,提示:“无法将类型为'System.String'的对象强制转换为类型'System.Reflection.PropertyInfo'”。 - VincentZHANG
显示剩余2条评论

1

有两种方法,使用值转换器或中间属性。最简单的可能是使用中间属性,因为您已经在使用良好结构化的绑定来绑定SelectedItem(但值转换器也很有趣!)。

SelectedValue绑定到FontColor,因此在setter中设置另一个值:

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");
    }
}

或者,如果您不想存储它:

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);;
    }
}

你可以在你的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}"/>

如果您有兴趣,值转换器将按如下方式工作:
在代码后台(或其他经常使用的地方)创建一个实现IValueConverter接口的类,该类将获取字符串并返回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();
    }
}

你可以将它用于XAML绑定中,将选定项绑定转换为前景画笔:

<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}}"/>

1
谢谢您详细的回答,但它只是在下拉列表折叠后着色所选项目,我想要的是该下拉列表中每个项目都被着色,请参考我问题中的图片。无论如何还是非常感谢您。 - VincentZHANG
啊,我的错,应该知道那会发生的。没有好好思考我的逻辑。 - Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接