2

This is a follow up to this question. I've developed a simple interface for a control that is supposed to allow a user to be able to define a color in the simplest way possible - by controlling the ARGB channels of the color itself.

enter image description here

I want this control to be able to bind directly to a color property so as to allow a user to adjust it via the sliders.

I've hit a wall - I've tried to implement MVVM with it but that does not work because in doing so I've utterly failed to be able to extract from the control the color property defined by the control itself.

I actually do not even feel that this is the correct way to be going about this. I need to have in place several controls that will allow users to change the behavior and look of our application, but I cannot figure out how to get a UserControl to bind to the settings of the application (I've been able to bind single, simple controls but when it comes to composite controls like this one, I'm getting no where).

This is the code for the control itself and the MVVM :

public partial class ColorDefiner : UserControl {

    public static readonly DependencyProperty
        _Color = DependencyProperty.Register( "Color", typeof( Color ), typeof( ColorDefiner ) );

    public Color Color {
        get { return ( Color )this.GetValue( ColorDefiner._Color ); }
        set { this.SetValue( ColorDefiner._Color, value ); }
    }

    public ColorDefiner( ) { InitializeComponent( ); }
}


public class ColorViewModel : INotifyPropertyChanged {

    public event PropertyChangedEventHandler PropertyChanged;

    private Color _Color = Colors.Black;

    public double A {
        get { return this.Color.ScA; }
        set {
            this._Color.ScA = ( float )value;
            if ( this.PropertyChanged != null ) {
                this.PropertyChanged( this, new PropertyChangedEventArgs( "A" ) );
                this.PropertyChanged( this, new PropertyChangedEventArgs( "Color" ) );
            }
        }
    }
    public double R {
        get { return this.Color.ScR; }
        set {
            this._Color.ScR = ( float )value;
            if ( this.PropertyChanged != null ) {
                this.PropertyChanged( this, new PropertyChangedEventArgs( "R" ) );
                this.PropertyChanged( this, new PropertyChangedEventArgs( "Red" ) );
                this.PropertyChanged( this, new PropertyChangedEventArgs( "Color" ) );
            }
        }
    }
    public double G {
        get { return this.Color.ScG; }
        set {
            this._Color.ScG = ( float )value;
            if ( this.PropertyChanged != null ) {
                this.PropertyChanged( this, new PropertyChangedEventArgs( "G" ) );
                this.PropertyChanged( this, new PropertyChangedEventArgs( "Green" ) );
                this.PropertyChanged( this, new PropertyChangedEventArgs( "Color" ) );
            }
        }
    }
    public double B {
        get { return this._Color.ScB; }
        set {
            this._Color.ScB = ( float )value;
            if ( this.PropertyChanged != null ) {
                this.PropertyChanged( this, new PropertyChangedEventArgs( "B" ) );
                this.PropertyChanged( this, new PropertyChangedEventArgs( "Blue" ) );
                this.PropertyChanged( this, new PropertyChangedEventArgs( "Color" ) );
            }
        }
    }

    public Color Color {
        get { return this._Color; }
        set {
            this._Color = value;
            if ( this.PropertyChanged != null )
                this.AllChanged( );
        }
    }
    public Color Red { get { return Color.FromScRgb( 1.0F, ( float )this.R, 0.0F, 0.0F ); } }
    public Color Green { get { return Color.FromScRgb( 1.0F, 0.0F, ( float )this.G, 0.0F ); } }
    public Color Blue { get { return Color.FromScRgb( 1.0F, 0.0F, 0.0F, ( float )this.B ); } }

    private void AllChanged( ) {
        this.PropertyChanged( this, new PropertyChangedEventArgs( "A" ) );
        this.PropertyChanged( this, new PropertyChangedEventArgs( "R" ) );
        this.PropertyChanged( this, new PropertyChangedEventArgs( "G" ) );
        this.PropertyChanged( this, new PropertyChangedEventArgs( "B" ) );
        this.PropertyChanged( this, new PropertyChangedEventArgs( "Red" ) );
        this.PropertyChanged( this, new PropertyChangedEventArgs( "Green" ) );
        this.PropertyChanged( this, new PropertyChangedEventArgs( "Blue" ) );
        this.PropertyChanged( this, new PropertyChangedEventArgs( "Color" ) );
    }
}

I've tried binding the Color dependency property to the Color View Model in code; I've tried binding it via a Style Setter in XAML :

<UserControl.Resources>
    <Style TargetType="Controls:ColorDefiner">
        <Setter Property="Color" Value="{Binding Color, Mode=TwoWay}"/>
    </Style>
</UserControl.Resources>

Nothing works - What is the appropriate way to do this? (or the best way, or the most proper way, or the most commonly accepted practice?) How do I extract defined Color property from the Color View Model attached to the control? Is that even the right way to go about doing this?

Community
  • 1
  • 1
Will
  • 3,413
  • 7
  • 50
  • 107
  • how do you know that VM Color property is not updating your View's Color property? If you have bound the properties, then SetValue() of DP is called internally, not the setter on your class. – Nitin Apr 21 '15 at 05:27
  • Damnit, other Will! You're making us look bad. If you're going to use a DependencyProperty, you have to use it correctly. _Color isn't right. Also, your understanding of how the Path works in Binding is incorrect. It's based off the current value of the DataContext, not the current instance. You'd have to rebase the path to the root of the UserControl by specifying and x:Name and Binding.ElementName or by using a RelativeSource of FindAncestor of x:Type UserControl. Oh well. At least you didn't create a ViewModel for your UserControl, so you've got that going for you. –  Apr 21 '15 at 14:26

2 Answers2

5

Your ColorDefiner control does not react when the Color property has changed. It should register a PropertyChangedCallback with dependency property metadata. The property metadata could also be used to specify that the property binds two-way by default. You should also follow naming conventions in WPF, and name the DependencyProperty field ColorProperty:

public partial class ColorDefiner : UserControl
{
    public static readonly DependencyProperty ColorProperty =
        DependencyProperty.Register(
            "Color", typeof(Color), typeof(ColorDefiner),
            new FrameworkPropertyMetadata(
                Colors.Black,
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                (o, e) => ((ColorDefiner)o).ColorPropertyChanged((Color)e.NewValue)));

    public Color Color
    {
        get { return (Color)GetValue(ColorProperty); }
        set { SetValue(ColorProperty, value); }
    }

    public ColorDefiner()
    {
        InitializeComponent();
    }

    private void ColorPropertyChanged(Color color)
    {
        sliderA.Value = (double)color.A;
        sliderR.Value = (double)color.R;
        sliderG.Value = (double)color.G;
        sliderB.Value = (double)color.B;
    }

    private void SliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        Color = Color.FromArgb((byte)sliderA.Value,
            (byte)sliderR.Value, (byte)sliderG.Value, (byte)sliderB.Value);
    }
}

The SliderValueChanged event handler is used for all four Sliders in the control's XAML:

<UserControl  ...>
    <StackPanel>
        <Slider x:Name="sliderA" Maximum="255" ValueChanged="SliderValueChanged"/>
        <Slider x:Name="sliderR" Maximum="255" ValueChanged="SliderValueChanged"/>
        <Slider x:Name="sliderG" Maximum="255" ValueChanged="SliderValueChanged"/>
        <Slider x:Name="sliderB" Maximum="255" ValueChanged="SliderValueChanged"/>
    </StackPanel>
</UserControl>

This simple example shows how the control works:

<Grid>
    <Grid.Background>
        <SolidColorBrush x:Name="brush" Color="AliceBlue"/>
    </Grid.Background>
    <local:ColorDefiner Color="{Binding Color, ElementName=brush}"/>
</Grid>

It could similarly be bound to any view model with a Color property.

Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Could you briefly explain how I would bind the Color property of this control a like property in the User Settings of an application? – Will Apr 22 '15 at 00:47
  • Have you searched StackOverflow (or the web) for how to bind to Settings properties? There are plenty of answers. – Clemens Apr 22 '15 at 06:40
-1

Create Color UserColorSelected property:

public Color UserColorSelected
{
   get { return userColorSelected;}
   set{
         userColorSelected=value;                           
         this.PropertyChanged(this, new PropertyChangedEventArgs("UserControlSelected"));

       }

And each of the ARGB property, set the UserColorSelected instance.

 public int A
    {
        get
        {

            return a;
        }
        set
        {
            a = value;
            this.UserColorSelected = Color.FromArgb(value, this.R, this.G, this.B);
            this.PropertyChanged(this, new PropertyChangedEventArgs("A"));

        }
    }

Do, similar for property R, G, and B.

ANewGuyInTown
  • 5,957
  • 5
  • 33
  • 45