0

I have a MultiValueConverter returning one of two SolidColoBrush passed as values[] elements depending on a bool passed as values[] elment as well.

All values[] elements are DependencyProperty.

The problem is that when I bind a Background property to these SolidColorBrush through this MultiValueConverter i get a cast error.

If I bind the SolidColorBrush.Color property to these SolidColorBrush (which seems an error to me unless there is an implicit conversion somewhere) everything works fine at runtime, but the designer does not show the desired Background, it shows always the background associated with the "false" value of my DependencyProperty even if I set the property to true.

The code is this:

XAML - UserControl

<UserControl.Resources>
    <local:State2BackgroundConverter x:Key="state2BackgroundConverter"/>
</UserControl.Resources>

<Grid.Background>
     <SolidColorBrush>
          <SolidColorBrush.Color>
               <MultiBinding Converter="{StaticResource state2BackgroundConverter}">
                   <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}" Path="Status"/>
                   <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}" Path="BrushOFF"/>
                   <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}" Path="BrushON"/>
               </MultiBinding>
          </SolidColorBrush.Color>
      </SolidColorBrush>
</Grid.Background>  // <--- This works at runtime but not at design time

<Grid.Background>
      <MultiBinding Converter="{StaticResource state2BackgroundConverter}">
          <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}" Path="Status"/>
          <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}" Path="BrushOFF"/>
          <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}" Path="BrushON"/>
      </MultiBinding>
 </Grid.Background>  // <--- This generates a cast error

XAML - Window

<LightControls:MyButton Margin="1,0,0,0" Height="80" HorizontalAlignment="Left" Width="80" BrushON="#FF7CFEFF" BrushOFF="#FF0096FF" Status="True"/>

C#

public class Status2ColorConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if ((bool)values[0] == true)
            return ((SolidColorBrush)values[2]);
        else
            return ((SolidColorBrush)values[1]);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return (null);
    }
}

private static readonly DependencyProperty StatusProperty = 
DependencyProperty.Register("Status",
                                    typeof(bool),
                                    typeof(MyButton),
                                    new PropertyMetadata(tre)
                                    );
    public bool Status
    {
        get { return (bool)this.GetValue(StatusProperty); }
        set { this.SetValue(StatusProperty, value); }
    }

    private static readonly DependencyProperty BrushONProperty = DependencyProperty.Register("BrushON",
                                    typeof(SolidColorBrush),
                                    typeof(MyButton),
                                    new PropertyMetadata(new SolidColorBrush(Colors.Cyan))
                                    );
    public SolidColorBrush BrushON
    {
        get { return (SolidColorBrush)this.GetValue(BrushONProperty); }

        set { this.SetValue(BrushONProperty, value); }
    }

    private static readonly DependencyProperty BrushOFFProperty = DependencyProperty.Register("BrushOFF",
                                    typeof(SolidColorBrush),
                                    typeof(MyButton),
                                    new PropertyMetadata(new SolidColorBrush(Colors.Black))
                                    );
    public SolidColorBrush BrushOFF
    {
        get { return (SolidColorBrush)this.GetValue(BrushOFFProperty); }

        set { this.SetValue(BrushOFFProperty, value); }
    }

So my questions are:

  1. Why I have to bind SolidColorBrush.Color property even if my BrushON/BrushOFF return SolidColorBrush and not SolidColorBrush.Color?
  2. How I can get this to work at design time? (NOTE: all the other Dependency Property in my project, including those having a custom ValueConverter, not MultiValueConverter, work just fine at design time, even other Background properties)

Thanks in advance!

Boltzmann
  • 31
  • 6
  • All this seems overly complicated, when you could simply have a PropertyChanged callback for the Status property, which directly sets the Grid Background to either of the two colors. – Clemens Oct 28 '17 at 10:35
  • I am quite new to WPF so please be patient, I alredy used propertyChanged callbacks (not shown here) to execute some code behind but this will not show the desired Background at design time (it does at run time), so I have to ask if you can suggest some resource where I can learn what I am missing here. However I will leave this question because I would like to understand where the error is, even if there is an easier implementation. Thank you very much! – Boltzmann Oct 28 '17 at 10:52
  • As another note, if there is a property called `ColorON`, I wouldn't expect it to be of type `SolidColorBrush`. Its type should be `Color` instead, which would also make your code somewhat simpler. – Clemens Oct 28 '17 at 10:54
  • You are right, I edited the question. I changed the name and not the type because that Dependency Property in general can be used as SolidColorBrush also for other purposes in the project. – Boltzmann Oct 28 '17 at 11:17
  • Then why restrict it to a SolidColorBrush? Brush properties like Background and Fill are typically declared as `Brush`, so that you can also assign Gradient- and ImageBrushes. – Clemens Oct 28 '17 at 11:19
  • Because I need it to be a SolidColorBrush – Boltzmann Oct 28 '17 at 12:57

1 Answers1

0

For those who encounter the same problem, I found an answer to point 2 of my question. Differently from what happends with IValueConverter, IMultiValueConverter can not evaluate a Dependency Property at design time, even if it has a default value.

Here is the link that gave me the right idea:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/9b902711-2c45-49f9-9478-b36f1e842f0b/imultivalueconverter-error-crashing-visual-studio-at-design-time?forum=wpf

In this article the "as" operator is proposed as solution because it sanifies null variables.

The variable that store the Dependency Property value in my code should not be null at design time (the dependency property has a default value in fact when the same dependency property is used as IValueConverter value everything works fine), so the right thing to do is to prevent this kind of designer misbehaviour by checking if values[] is null and assigning a default return value in that case.

As an example (there could be many smarter ways to obtain the same result):

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        string s = values[0] as string;
        if (s==null)
        {
            return (SomeDefaultValue); 
        }
        else
        { 
            Actually do something
        }
    }
Boltzmann
  • 31
  • 6