How can I convert the values of three sliders into a Color?

前端 未结 2 1311
执念已碎
执念已碎 2020-12-22 07:29

I\'m trying to create a custom user control that will allow a user to define a color in WPF. I\'ve done this before in WinForms but in WPF it seems to be not as straight for

相关标签:
2条回答
  • 2020-12-22 07:41

    Okay - I want to thank everyone for their help; I was finally able to come up with a solution - in the event anyone is interested or stumbles upon this, it's also a good lesson in MVVM (maybe; I don't know...).

    Anyway :

    This is the MVVM I implemented :

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

    This is the control class :

    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 ); }
        }
    
        private ColorViewModel CVM { get { return this.DataContext as ColorViewModel; } }
    
        public ColorDefiner( ) {
            InitializeComponent( );
            Binding B = new Binding( "Color" ) { Source = this.DataContext };
            this.SetBinding( ColorDefiner._Color, B );
        }
    }
    

    This is the XAML for the User Control (yes, I did it with a UserControl; this required a data context, and I really don't want to fuss with that yet in a Custom Control - this is really only going to be my own personal control for my own use anyway) :

    <UserControl
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:Components="clr-namespace:WPFTools.Components"
        xmlns:Controls="clr-namespace:WPFTools.Controls"
        x:Class="WPFTools.Controls.ColorDefiner"
        mc:Ignorable="d"
        Width="100" Height="100" FontFamily="Arial" FontWeight="Bold">
        <UserControl.DataContext>
            <Controls:ColorViewModel/>
        </UserControl.DataContext>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="20"/>
                <RowDefinition Height="5"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Components:WPFGLabel Text="A" StrokeThickness="0.5" TextAlignment="Right" Stroke="#99000000">
                    <Components:WPFGLabel.Fill>
                        <SolidColorBrush Color="{Binding Color}"/>
                    </Components:WPFGLabel.Fill>
                </Components:WPFGLabel>
            </Viewbox>
            <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1">
                <Components:WPFGLabel Text="R" StrokeThickness="0.5" TextAlignment="Right" Stroke="#99000000">
                    <Components:WPFGLabel.Fill>
                        <SolidColorBrush Color="{Binding Red, Mode=OneWay}"/>
                    </Components:WPFGLabel.Fill>
                </Components:WPFGLabel>
            </Viewbox>
            <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="2">
                <Components:WPFGLabel Text="G" StrokeThickness="0.5" TextAlignment="Right" Stroke="#99000000">
                    <Components:WPFGLabel.Fill>
                        <SolidColorBrush Color="{Binding Green, Mode=OneWay}"/>
                    </Components:WPFGLabel.Fill>
                </Components:WPFGLabel>
            </Viewbox>
            <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="3">
                <Components:WPFGLabel Text="B" StrokeThickness="0.5" TextAlignment="Right" Stroke="#99000000" RenderTransformOrigin="-7.272,0.575">
                    <Components:WPFGLabel.Fill>
                        <SolidColorBrush Color="{Binding Blue, Mode=OneWay}"/>
                    </Components:WPFGLabel.Fill>
                </Components:WPFGLabel>
            </Viewbox>
            <Viewbox HorizontalAlignment="Stretch" Grid.Row="2" VerticalAlignment="Stretch">
                <Slider Orientation="Vertical" TickPlacement="Both" LargeChange="0.1" Maximum="1" SmallChange="0.01" TickFrequency="0.02" Height="90" TabIndex="0" Value="{Binding A, Mode=TwoWay}"/>
            </Viewbox>
            <Viewbox HorizontalAlignment="Stretch" Grid.Row="2" Grid.Column="1"  VerticalAlignment="Stretch">
                <Slider Orientation="Vertical" TickPlacement="Both" LargeChange="0.1" Maximum="1" SmallChange="0.01" TickFrequency="0.02" Height="90" TabIndex="1" Value="{Binding R, Mode=TwoWay}"/>
            </Viewbox>
            <Viewbox HorizontalAlignment="Stretch" Grid.Row="2" Grid.Column="2"  VerticalAlignment="Stretch">
                <Slider Orientation="Vertical" TickPlacement="Both" LargeChange="0.1" Maximum="1" SmallChange="0.01" TickFrequency="0.02" Height="90" TabIndex="2" Value="{Binding G, Mode=TwoWay}"/>
            </Viewbox>
            <Viewbox HorizontalAlignment="Stretch" Grid.Row="2" Grid.Column="3"  VerticalAlignment="Stretch">
                <Slider Orientation="Vertical" TickPlacement="Both" LargeChange="0.1" Maximum="1" SmallChange="0.01" TickFrequency="0.02" Height="90" Value="{Binding B, Mode=TwoWay}"/>
            </Viewbox>
        </Grid>
    </UserControl>
    

    I bound the Brush Color of the Labels to their respective colors in the View Model (A got the entire thing). I Two-Way Bound the value of each slider to it's respective value within the view model, and in the code I bound the Dependency Property to the view model Color property. I tested this, and it worked.

    Once again, thank you all for your help.

    0 讨论(0)
  • 2020-12-22 08:04

    First of all, I'd recommend making these changes to your ByteToColorConverter:

    public class DoubleToColorConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // Values bound to sliders are going to be doubles.
            return Color.FromScRgb((float)(double)values[0], (float)(double)values[1], (float)(double)values[2], (float)(double)values[3]);
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            Color C = (Color)value;
            return new object[] { (double)C.ScA, (double)C.ScR, (double)C.ScG, (double)C.ScB };
        }
    }
    

    I've switched from bytes to doubles, as the slider value you're trying to bind to will only return/accept doubles. The (float)(double) cast is to deal with unboxing the values in the array.

    With this XAML, I was able to get a basic ARGB colour mixer working. Notice I've changed the Min/Max values on the slider, now we're not dealing with bytes anymore.

    <StackPanel>
        <Slider x:Name="sdrAlpha" Height="32" LargeChange="0.5" SmallChange="0.1" Minimum="0" Maximum="1" Width="321" TickPlacement="Both"/>
        <Slider x:Name="sdrRed" Height="32" LargeChange="0.5" SmallChange="0.1" Minimum="0" Maximum="1" Width="321" TickPlacement="Both"/>
        <Slider x:Name="sdrGreen" Height="32" LargeChange="0.5" SmallChange="0.1" Minimum="0" Maximum="1" Width="321" TickPlacement="Both"/>
        <Slider x:Name="sdrBlue" Height="32" LargeChange="0.5" SmallChange="0.1" Minimum="0" Maximum="1" Width="321" TickPlacement="Both"/>
        <Border x:Name="colourBorder" Height="200" HorizontalAlignment="Stretch">
            <Border.Background>
                <SolidColorBrush>
                    <SolidColorBrush.Color>
                        <MultiBinding Converter="{StaticResource colorConverter}">
                            <Binding ElementName="sdrAlpha" Path="Value" Mode="TwoWay" />
                            <Binding ElementName="sdrRed" Path="Value" Mode="TwoWay" />
                            <Binding ElementName="sdrGreen" Path="Value" Mode="TwoWay" />
                            <Binding ElementName="sdrBlue" Path="Value" Mode="TwoWay" />
                        </MultiBinding>
                    </SolidColorBrush.Color>
                </SolidColorBrush>
            </Border.Background>
        </Border>
    </StackPanel>
    

    If you wanted to bind a single slider to the converter, you could update your converter to inspect the number of values in the values[] array. You could perhaps use the ConverterParameter to pass in the colour you'd like that single slider to affect ... something like this:

    <MultiBinding Converter="{StaticResource colorConverter}" ConverterParameter="Red">
        <Binding ElementName="sdrRed" Path="Value" Mode="TwoWay" />
    </MultiBinding>
    
    0 讨论(0)
提交回复
热议问题