0

In my View, I have four sliders that represent the four channels of a color. The color itself is represented as a Vector4 in my View Model.

How do I write a Converter that "splits" the Vector into it's components so each slider changes a single channel of the color?

My idea was to create one Converter for each channel (Below is for the red channel. The other channels are analogue to the Red converter):

public class ColorToMediaRConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, ystem.Globalization.CultureInfo culture)
    {
        //Makes sense, Gets the Red Component
        Vector4 color = (Vector4)value;
        return color.X * 255;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        //The value is a float here, it does not make sense to make Vector4 out of it
        return null;
    }
}

Here is the corresponding XAML:

<pt:ColorSlider LeftColor="Black" RightColor="Red" Value="{Binding DiffuseColor,Converter={StaticResource MediaColorRConverter}}" ></pt:ColorSlider>
<pt:ColorSlider LeftColor="Black" RightColor="Green" Value="{Binding DiffuseColor,Converter={StaticResource MediaColorGConverter}}"></pt:ColorSlider>
<pt:ColorSlider LeftColor="Black" RightColor="Blue" Value="{Binding DiffuseColor,Converter={StaticResource MediaColorBConverter}}"></pt:ColorSlider>
<pt:ColorSlider LeftColor="Transparent" RightColor="White" Value="{Binding DiffuseColor,Converter={StaticResource MediaColorAConverter}}"></pt:ColorSlider>
<Rectangle Height="50" Width="50">
    <Rectangle.Fill>
        <SolidColorBrush Color="{Binding DiffuseColor,Converter={StaticResource MediaColorConverter}}"/>
    </Rectangle.Fill>
</Rectangle>

My ViewModel looks like this:

public Vector4 DiffuseColor
{
    get
    {
        return _material.DiffuseColor;
    }
    set
    {
        _material.DiffuseColor = value;
        NotifyPropertyChanged(nameof(DiffuseColor));
    }
}

When I change the sliders, nothing happens, the Setter of DiffuseColor does not execute.

Raildex
  • 3,406
  • 1
  • 18
  • 42
  • Maybe this is helpful https://stackoverflow.com/questions/11263387/is-it-possible-to-return-more-than-one-value-from-an-ivalueconverter-in-wpf – DrkDeveloper Feb 19 '23 at 12:53
  • DiffuseColor is the source property in your "Value="{Binding DiffuseColor" binding. The source property is assigned after the ConvertBack method of the converter. And you, in fact, this method is not implemented. Therefore, the sliders do not assign their own DiffuseColor values. The implementation of "pt:ColorSlider" and "Vector4" also raises a number of questions. To convert "Vector4" to a color, you need to change not one of its properties, but replace the entire instance. A struct is best suited for this, not a class. – EldHasp Feb 19 '23 at 14:04
  • But in the structure, you won't be able to change individual properties using the sliders. Therefore, you have two implementation paths. Or make in the ViewModel (or in DiffuseColor) separate properties for each component, listen for their changes and form a complex value that can be converted to color (for example, string). Or use a multi converter that will take the values directly from the Sliders and convert it to a color. – EldHasp Feb 19 '23 at 14:08
  • To get the simplest solution, your Vector4 type should expose four properties: `int R { get; set; }`, `int G { get; set; }`, `int B { get; set; }` and a computed property that computes the actual `Color` from the `R`, `G` and `B` values: `Color RgbColor => Color.FromRgb((byte)R, (byte)G, (byte)B);`. Setting R, G and B should raise PropertyChanged for the RgbColor property too. Then simply bind each slider to a RGB property and the SolidColorBrush to the RgbColor property - no converters needed. – BionicCode Feb 19 '23 at 19:21

1 Answers1

0

A Multi Value Converter solution will be as following:

<Grid>
        <Grid.Resources>
            <local:ColorConverter x:Key="colorConverter"/>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Slider Minimum="0" Maximum="255" Name="red" Grid.Row="0" Value="33" Background="Red"/>
        <Slider Minimum="0" Maximum="255" Name="green" Grid.Row="1" Background="Green"/>
        <Slider Minimum="0" Maximum="255" Name="blue" Grid.Row="2" Background="Blue"/>
        <Rectangle  Grid.Row="3">
            <Rectangle.Fill>
                <SolidColorBrush >
                    <SolidColorBrush.Color>
                        <MultiBinding Converter="{StaticResource colorConverter}">
                            <Binding Path="Value" ElementName="red" />
                            <Binding Path="Value" ElementName="green"/>
                            <Binding Path="Value" ElementName="blue"/>
                        </MultiBinding>
                    </SolidColorBrush.Color>
                </SolidColorBrush>
            </Rectangle.Fill>
        </Rectangle>

    </Grid>

The sample Converter code is as following:

public class ColorConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {

        if (values != null)
        {
           if (values.Length ==3)
            {
                byte red = System.Convert.ToByte(values[0]);
                byte green = System.Convert.ToByte(values[1]);
                byte blue = System.Convert.ToByte(values[2]);
                Color color = new Color() {R = red,G= green,B = blue ,  A=255};
                return color;

            }
        }
        return new Color() {  R=1,G=2,B=3};
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
    throw ( new NotImplementedException());
}
Gilad Waisel
  • 120
  • 4
  • .NET defines a `NotImplementedException`. The stack trace will look different if you throw specialized exceptions. Since the not implemented exception is meant to be used during development, you should consider to throw the `NotSupportedException` to signal that the functionality is not missing the implementation but is deliberately not supported. – BionicCode Feb 19 '23 at 19:28