将颜色传递给IValueConverter

时间:2016-06-23 17:26:14

标签: c# wpf mvvm ivalueconverter

我正在使用MVVM设计模式制作WPF应用程序。应用程序的一部分是信号强度条。我们只用一个矩形用户控件创建了它,并创建了一个4列网格,所以我们需要做的就是改变控件的背景颜色或前景颜色。

我对如何执行此操作的想法只是为4个部分中的每个部分存储布尔值并使用值转换器。但是,此控件有3个实例,每个实例都有不同的颜色。如何将所需的颜色传递到转换器?我知道转换器有一个参数参数,但我还没有找到任何使用它的例子,所以我甚至不确定参数参数是否是我正在寻找的。

2 个答案:

答案 0 :(得分:3)

您的案例可能无法通过您选择的方法得到最佳解决(这使得难以对段的颜色进行参数化),但您的具体问题很好,所以我会回答它。

正如您所发现的那样,将一个字符串传递给ConverterParameter是很困难的。但你不必这样做。如果从MarkupExtension派生转换器,则可以在使用它时分配命名和类型属性,也不必将其创建为资源(实际上,将其创建为资源会破坏事物,因为那样做是一个共享实例,属性在创建时初始化)。由于XAML解析器知道在类上声明的属性的类型,因此它将对TypeConverter应用默认的Brush,并且您将获得与分配{{}时完全相同的行为1}}到"PapayaWhip"或其他任何内容。

这适用于任何类型,当然,不仅仅是"Border.Background"

Brush

用法:

namespace HollowEarth.Converters
{
    public class BoolBrushConverter : MarkupExtension, IValueConverter
    {
        public Brush TrueBrush { get; set; }
        public Brush FalseBrush { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return System.Convert.ToBoolean(value) ? TrueBrush : FalseBrush;
        }

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

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}

你也可以给<TextBox xmlns:hec="clr-namespace:HollowEarth.Converters" Foreground="{Binding MyFlagProp, Converter={hec:BoolBrushConverter TrueBrush=YellowGreen, FalseBrush=DodgerBlue}}" /> 一个带参数的构造函数。

BoolBrushConverter

在XAML ......

public BoolBrushConverter(Brush tb, Brush fb)
{
    TrueBrush = tb;
    FalseBrush = fb;
}

我认为这不适合这种情况。但有时语义是如此清晰,属性名称是不必要的。例如<TextBox xmlns:hec="clr-namespace:HollowEarth.Converters" Foreground="{Binding MyFlagProp, Converter={hec:BoolBrushConverter YellowGreen, DodgerBlue}}" />

更新

这是SignalBars控件的完整实现。这有四个段到你的四个,但你可以轻松删除一个;仅在模板中,{hec:GreaterThan 4.5}属性为Value,可以按照您喜欢的方式细分(再次,在模板中)。

SignalBars.cs

double

主题\ Generic.xaml

using System;
using System.ComponentModel;
using System.Windows.Media;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;

namespace HollowEarth
{
    public class SignalBars : ContentControl
    {
        static SignalBars()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(SignalBars), new FrameworkPropertyMetadata(typeof(SignalBars)));
        }

        #region Value Property
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(SignalBars),
                new PropertyMetadata(0d));
        #endregion Value Property

        #region InactiveBarFillBrush Property
        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("White")]
        public Brush InactiveBarFillBrush
        {
            get { return (Brush)GetValue(InactiveBarFillBrushProperty); }
            set { SetValue(InactiveBarFillBrushProperty, value); }
        }

        public static readonly DependencyProperty InactiveBarFillBrushProperty =
            DependencyProperty.Register("InactiveBarFillBrush", typeof(Brush), typeof(SignalBars),
                new FrameworkPropertyMetadata(Brushes.White));
        #endregion InactiveBarFillBrush Property
    }

    public class ComparisonConverter : MarkupExtension, IMultiValueConverter
    {
        public virtual object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length != 2)
            {
                throw new ArgumentException("Exactly two values are expected");
            }

            var d1 = GetDoubleValue(values[0]);
            var d2 = GetDoubleValue(values[1]);

            return Compare(d1, d2);
        }

        /// <summary>
        /// Overload in subclasses to create LesserThan, EqualTo, whatever.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        protected virtual bool Compare(double a, double b)
        {
            throw new NotImplementedException();
        }

        protected static double GetDoubleValue(Object o)
        {
            if (o == null || o == DependencyProperty.UnsetValue)
            {
                return 0;
            }
            else
            {
                try
                {
                    return System.Convert.ToDouble(o);
                }
                catch (Exception)
                {
                    return 0;
                }
            }
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }

    public class GreaterThan : ComparisonConverter
    {
        protected override bool Compare(double a, double b)
        {
            return a > b;
        }
    }
}

示例XAML:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >

    <Style 
        xmlns:he="clr-namespace:HollowEarth"
        TargetType="{x:Type he:SignalBars}" 
        >
        <!-- Foreground is the bar borders and the fill for "active" bars -->
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="InactiveBarFillBrush" Value="White" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Control">
                    <ControlTemplate.Resources>
                        <Style TargetType="Rectangle">
                            <Setter Property="Width" Value="4" />
                            <Setter Property="VerticalAlignment" Value="Bottom" />
                            <Setter Property="Stroke" Value="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
                            <Setter Property="StrokeThickness" Value="1" />
                            <Setter Property="Fill" Value="{Binding InactiveBarFillBrush, RelativeSource={RelativeSource TemplatedParent}}" />
                            <Setter Property="Margin" Value="0,0,1,0" />
                            <Style.Triggers>
                                <DataTrigger Value="True">
                                    <DataTrigger.Binding>
                                        <MultiBinding Converter="{he:GreaterThan}">
                                            <MultiBinding.Bindings>
                                                <Binding 
                                                    Path="Value" 
                                                    RelativeSource="{RelativeSource TemplatedParent}" 
                                                    />
                                                <Binding 
                                                    Path="Tag" 
                                                    RelativeSource="{RelativeSource Self}" 
                                                    />
                                            </MultiBinding.Bindings>
                                        </MultiBinding>
                                    </DataTrigger.Binding>
                                    <Setter Property="Fill" Value="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ControlTemplate.Resources>
                    <ContentControl
                        ContentTemplate="{Binding ContentTemplate, RelativeSource={RelativeSource TemplatedParent}}">
                        <StackPanel 
                            Orientation="Horizontal"
                            SnapsToDevicePixels="True"
                            UseLayoutRounding="True"
                            >
                            <!-- Set Tags to the minimum threshold value for turning the segment "on" -->
                            <!-- Remove one of these to make it four segments. To make them all equal height, remove Height here
                            and set a fixed height in the Rectangle Style above. -->
                            <Rectangle Height="4" Tag="0" />
                            <Rectangle Height="6" Tag="2" />
                            <Rectangle Height="8" Tag="4" />
                            <Rectangle Height="10" Tag="6" />
                            <Rectangle Height="12" Tag="8" />
                        </StackPanel>
                    </ContentControl>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

答案 1 :(得分:0)

通常您可能需要ColorToBrushConverter,但不需要BooleanToColor。

我只想为每个栏创建不同的触发器样式,例如

        <Style.Triggers>
            <DataTrigger Binding="{Binding IsOffline}" Value="True">
                <Setter Property="Background" Value="Salmon" />
            </DataTrigger>
            <DataTrigger Binding="{Binding IsPrinting}" Value="True">
                <!--<Setter Property="Background" Value="Honeydew" />-->
                <Setter Property="Background" Value="LightGreen" />
            </DataTrigger>
        </Style.Triggers>