2016-06-23 4 views
0

Я делаю приложение WPF с использованием шаблона проектирования MVVM. Часть приложения - это индикатор силы сигнала. Мы создали его с помощью прямоугольного пользовательского элемента управления и создали сетку с четырьмя столбцами, поэтому все, что нам нужно сделать, это изменить цвет фона или переднего плана элемента управления.Pass Color to IValueConverter

Моя идея о том, как это сделать, - это просто сохранить логические значения для каждого из четырех разделов и использовать преобразователь значений. Однако есть три экземпляра этого элемента управления, каждый из которых имеет другой цвет. Как передать необходимый цвет в конвертер? Я знаю, что у преобразователей есть аргумент параметра, но я не смог найти какие-либо примеры, используя его, поэтому я даже не уверен, что аргумент параметра - это то, что я ищу.

+0

Как используется преобразователь? Я не понимаю, что вы имеете в виду для изменения фона управления, чтобы изменить бары. Несколько недель назад я написал пятиступенчатый контроль силы сигнала. Помогло бы мне, если бы я просто вставлял это? Вы можете тривиально изменить шаблон, чтобы изменить счетчик сегментов или что-то еще. –

+0

Я полагаю, это возможно. Я решил, что конвертер просто посмотрит на логическое значение для этого раздела и изменит фон на прозрачный или непрозрачный. – Eric

+0

Я сделал это, написав конвертер GreaterThan. Сегменты имеют триггер стиля, который устанавливает заполнение сегмента на кисть «on», если входное значение больше, чем «Tag» сегмента. –

ответ

3

Ваш случай не может быть лучше всего решать с помощью метода вы выбрали (это делает его трудно спараметрировать цвета сегментов), но ваш конкретный вопрос хороший, поэтому я отвечу на него.

Как вы нашли, трудно передать ничего, кроме строки, до ConverterParameter. но вам это не нужно. Если вы получаете конвертер от MarkupExtension, вы можете назначать именованные и типизированные свойства при его использовании, а также не создавать его в качестве ресурса (действительно, создание его в качестве ресурса приведет к поломке вещи, поскольку это будет общий экземпляр и свойства инициализируются, когда они созданы). Поскольку анализатор XAML знает типы свойств, объявленных в классе, он будет применять значение по умолчанию TypeConverter для Brush, и вы получите такое же поведение, как если бы вы назначали "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 конструктор, который принимает параметры, тоже.

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

И в XAML ...

<TextBox 
    xmlns:hec="clr-namespace:HollowEarth.Converters" 
    Foreground="{Binding MyFlagProp, Converter={hec:BoolBrushConverter YellowGreen, DodgerBlue}}" 
    /> 

Я не думаю, что это хорошо подходит для этого случая. Но иногда семантика настолько ясна, имя свойства не нужно. {hec:GreaterThan 4.5}, например.

UPDATE

Вот полная реализация управления SignalBars. Это пять сегментов для ваших четырех, но вы можете легко удалить их; это только в шаблоне, а свойство Value - это double, которое можно разделить так, как вам нравится (опять же, в шаблоне).

SignalBars.cs

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

Темы \ Generic.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> 

Пример XAML:

<StackPanel 
    xmlns:he="clr-namespace:HollowEarth" 
    Orientation="Vertical" 
    HorizontalAlignment="Left" 
    > 
    <Slider 
     Minimum="0" 
     Maximum="10" 
     x:Name="SignalSlider" 
     Width="200" 
     SmallChange="1" 
     LargeChange="4" 
     TickFrequency="1" 
     IsSnapToTickEnabled="True" 
     /> 
    <he:SignalBars 
     HorizontalAlignment="Left" 
     Value="{Binding Value, ElementName=SignalSlider}" 
     InactiveBarFillBrush="White" 
     Foreground="DarkRed" 
     /> 
</StackPanel> 
+0

Я буду исследовать ваш код и посмотреть, как/если я могу его адаптировать, чтобы делать то, что мне нужно. – Eric

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>