Search code examples
silverlightcontrolsexpression-blend

How to use the Expression Blend 4 ranged property control


Is there a way to use the nifty Expression Blend ranged value control in your own projects?

This one:

enter image description here

Edits:

I would really, really love to know.


Solution

  • I made an attempt a while back in WPF to remake the ColorPicker and I needed a similar control to enter the R, G, B and A values.

    ColorComponentSlider

    It's not complete but it gives a pretty good start. It supports dragging and typing but I picked a different dragging scheme because I dislike the Blend drag.

    Here's what I did:

    [TemplateVisualState(GroupName = "EditStates", Name = "DragMode")]
    [TemplateVisualState(GroupName = "EditStates", Name = "TypeMode")]
    public class ColorComponentSlider : Control
    {
        static ColorComponentSlider()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorComponentSlider), new FrameworkPropertyMetadata(typeof(ColorComponentSlider)));
        }
    
        private bool _isTemplateApplied = false;
        private TextBox _textBox;
        private TextBlock _textBlock;
        private GradientStop _gradientStart;
        private GradientStop _gradientEnd;
        private Grid _progressGrid;
        private bool _isDragging = false;
    
        public string Component
        {
            get { return (string)GetValue(ComponentProperty); }
            set { SetValue(ComponentProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for Component.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ComponentProperty =
            DependencyProperty.Register("Component", typeof(string), typeof(ColorComponentSlider), new UIPropertyMetadata("R", OnComponentPropertyChanged));
    
        private static void OnComponentPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var colorComponentSlider = sender as ColorComponentSlider;
            colorComponentSlider.UpdateChildControls();
        }
    
        public Color Color
        {
            get { return (Color)GetValue(ColorProperty); }
            set { SetValue(ColorProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for Color.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ColorProperty =
            DependencyProperty.Register("Color", typeof(Color), typeof(ColorComponentSlider), new UIPropertyMetadata(Colors.Black, OnColorPropertyChanged));
    
        private static void OnColorPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var colorComponentSlider = sender as ColorComponentSlider;
            colorComponentSlider.UpdateChildControls();
        }
    
        private void UpdateChildControls()
        {
            if (_isTemplateApplied)
            {
                double progress = 0;
                string colorComponent = String.Empty;
                var comp = Component.ToUpper();
                if (comp == "A")
                {
                    _gradientStart.Color = Color.FromArgb(0, Color.R, Color.G, Color.B);
                    _gradientEnd.Color = Color.FromArgb(255, Color.R, Color.G, Color.B);
                    colorComponent = Color.A.ToString();
                    progress = Color.A;
                }
                else if (comp == "R")
                {
                    _gradientStart.Color = Color.FromArgb(Color.A, 0, Color.G, Color.B);
                    _gradientEnd.Color = Color.FromArgb(Color.A, 255, Color.G, Color.B);
                    colorComponent = Color.R.ToString();
                    progress = Color.R;
                }
                else if (comp == "G")
                {
                    _gradientStart.Color = Color.FromArgb(Color.A, Color.R, 0, Color.B);
                    _gradientEnd.Color = Color.FromArgb(Color.A, Color.R, 255, Color.B);
                    colorComponent = Color.G.ToString();
                    progress = Color.G;
                }
                else if (comp == "B")
                {
                    _gradientStart.Color = Color.FromArgb(Color.A, Color.R, Color.G, 0);
                    _gradientEnd.Color = Color.FromArgb(Color.A, Color.R, Color.G, 255);
                    colorComponent = Color.B.ToString();
                    progress = Color.B;
                }
                if (colorComponent.Length > 0)
                {
                    _textBlock.Text = colorComponent;
                    _textBox.Text = colorComponent;
                    _progressGrid.ColumnDefinitions[0].Width = new GridLength(progress, GridUnitType.Star);
                    _progressGrid.ColumnDefinitions[1].Width = new GridLength(255 - progress, GridUnitType.Star);
                }
            }
        }
    
        private VisualStateGroup _editStates = null;
        private VisualState _dragModeState = null;
        private VisualState _typeModeState = null;
    
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
    
            _textBox = this.Template.FindName("textBox", this) as TextBox;
            _textBlock = this.Template.FindName("textBlock", this) as TextBlock;
            _gradientStart = this.Template.FindName("PART_GradientStart", this) as GradientStop;
            _gradientEnd = this.Template.FindName("PART_GradientEnd", this) as GradientStop;
            _progressGrid = this.Template.FindName("PART_Progress", this) as Grid;
    
            var root = this.Template.FindName("root", this) as FrameworkElement;
            var stateGroups = VisualStateManager.GetVisualStateGroups(root);
            for (int i = 0; i < stateGroups.Count; i++)
            {
                var stateGroup = stateGroups[i] as VisualStateGroup;
                if (stateGroup != null && stateGroup.Name == "EditStates")
                {
                    _editStates = stateGroup;
                    for (int s = 0; s < _editStates.States.Count; s++)
                    {
                        var state = _editStates.States[s] as VisualState;
                        if (state.Name == "DragMode")
                        {
                            _dragModeState = state;
                        }
                        else if (state.Name == "TypeMode")
                        {
                            _typeModeState = state;
                        }
                    }
                    break;
                }
            }
            VisualStateManager.GoToState(this, _dragModeState.Name, false);
            _isTemplateApplied = true;
            UpdateChildControls();
        }
    
        private Point _mouseLeftButtonDownPreviousPosition;
    
        protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            if (_editStates.CurrentState == _dragModeState)
            {
                _mouseLeftButtonDownPreviousPosition = e.GetPosition(this);
                Mouse.Capture(this);
            }
        }
    
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (_editStates.CurrentState == _dragModeState && e.LeftButton == MouseButtonState.Pressed)
            {
                _isDragging = true;
                var position = e.GetPosition(this);
    
                int offset = (int)(_mouseLeftButtonDownPreviousPosition.Y - position.Y);
    
                string comp = Component.ToUpper();
    
                if (comp == "A")
                {
                    int v;
                    if (Color.A + offset < 0)
                    {
                        v = 0;
                    }
                    else if (Color.A + offset > 255)
                    {
                        v = 255;
                    }
                    else
                    {
                        v = Color.A + offset;
                    }
                    Color = Color.FromArgb((byte)v, Color.R, Color.G, Color.B);
                }
                else if (comp == "R")
                {
                    int v;
                    if (Color.R + offset < 0)
                    {
                        v = 0;
                    }
                    else if (Color.R + offset > 255)
                    {
                        v = 255;
                    }
                    else
                    {
                        v = Color.R + offset;
                    }
                    Color = Color.FromArgb(Color.A, (byte)v, Color.G, Color.B);
                }
                else if (comp == "G")
                {
                    int v;
                    if (Color.G + offset < 0)
                    {
                        v = 0;
                    }
                    else if (Color.G + offset > 255)
                    {
                        v = 255;
                    }
                    else
                    {
                        v = Color.G + offset;
                    }
                    Color = Color.FromArgb(Color.A, Color.R, (byte)v, Color.B);
                }
                else if (comp == "B")
                {
                    int v;
                    if (Color.B + offset < 0)
                    {
                        v = 0;
                    }
                    else if (Color.B + offset > 255)
                    {
                        v = 255;
                    }
                    else
                    {
                        v = Color.B + offset;
                    }
                    Color = Color.FromArgb(Color.A, Color.R, Color.G, (byte)v);
                }
    
                _mouseLeftButtonDownPreviousPosition = position;
            }
            else
            {
                _isDragging = false;
            }
    
        }
        protected override void OnMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e);
            if (_editStates.CurrentState == _dragModeState)
            {
                var position = e.GetPosition(this);
                Mouse.Capture(null);
                if (!_isDragging && _mouseLeftButtonDownPreviousPosition == position)
                {
                    VisualStateManager.GoToState(this, _typeModeState.Name, true);
                    _textBox.SelectAll();
                }
                _isDragging = false;
            }
        }
    
        protected override void OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e)
        {
            base.OnGotKeyboardFocus(e);
            if (_editStates.CurrentState == _dragModeState)
            {
                VisualStateManager.GoToState(this, _typeModeState.Name, true);
            }
        }
    
        protected override void OnLostKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e)
        {
            base.OnLostKeyboardFocus(e);
            if (_editStates.CurrentState == _typeModeState)
            {
                VisualStateManager.GoToState(this, _dragModeState.Name, true);
    
                byte v;
                if (Byte.TryParse(_textBox.Text, out v))
                {
                    string comp = Component.ToUpper();
    
                    if (comp == "A")
                    {
                        Color = Color.FromArgb((byte)v, Color.R, Color.G, Color.B);
                    }
                    else if (comp == "R")
                    {
                        Color = Color.FromArgb(Color.A, (byte)v, Color.G, Color.B);
                    }
                    else if (comp == "G")
                    {
                        Color = Color.FromArgb(Color.A, Color.R, (byte)v, Color.B);
                    }
                    else if (comp == "B")
                    {
                        Color = Color.FromArgb(Color.A, Color.R, Color.G, (byte)v);
                    }
                }
            }
        }
    }
    

    XAML:

    <Style BasedOn="{x:Null}"
           TargetType="{x:Type local:ColorComponentSlider}">
        <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
        <Setter Property="Background"
                Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
        <Setter Property="BorderBrush"
                Value="Black" />
        <Setter Property="BorderThickness"
                Value="1" />
        <Setter Property="Padding"
                Value="1" />
        <Setter Property="AllowDrop"
                Value="true" />
        <Setter Property="FocusVisualStyle"
                Value="{x:Null}" />
        <Setter Property="Focusable"
                Value="True" />
        <Setter Property="ScrollViewer.PanningMode"
                Value="VerticalFirst" />
        <Setter Property="Stylus.IsFlicksEnabled"
                Value="False" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ColorComponentSlider}">
                    <Border x:Name="root"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Background="{TemplateBinding Background}"
                            SnapsToDevicePixels="true"
                            CornerRadius="5"
                            ClipToBounds="True">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="EditStates">
                                <VisualState x:Name="DragMode" />
                                <VisualState x:Name="TypeMode">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                                                       Storyboard.TargetName="textBlock">
                                            <DiscreteObjectKeyFrame KeyTime="0"
                                                                    Value="{x:Static Visibility.Hidden}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                                                       Storyboard.TargetName="textBox">
                                            <DiscreteObjectKeyFrame KeyTime="0"
                                                                    Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="auto" />
                                <RowDefinition Height="*" MinHeight="5" />
                            </Grid.RowDefinitions>
                            <Grid  x:Name="PART_Progress"
                                   UseLayoutRounding="True"
                                   RenderOptions.EdgeMode="Unspecified">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>
                                <Border CornerRadius="3,3,0,0"
                                        Background="#FF666666" />
                            </Grid>
                            <TextBlock x:Name="textBlock"
                                       Background="{x:Null}"
                                       Foreground="{TemplateBinding Foreground}"
                                       Margin="0"
                                       Padding="2,2,0,0" />
                            <TextBox x:Name="textBox"
                                     Background="{x:Null}"
                                     BorderThickness="0"
                                     CaretBrush="{TemplateBinding Foreground}"
                                     Foreground="{TemplateBinding Foreground}"
                                     Margin="0"
                                     Padding="0,2,0,0"
                                     Visibility="Hidden" />
                            <Border Grid.Row="1"
                                    CornerRadius="0,0,3,3">
                                <Border.Background>
                                    <DrawingBrush TileMode="Tile"
                                                  Viewport="0,0,10,10"
                                                  ViewportUnits="Absolute">
                                        <DrawingBrush.Drawing>
                                            <DrawingGroup>
                                                <GeometryDrawing Brush="White">
                                                    <GeometryDrawing.Geometry>
                                                        <RectangleGeometry Rect="0,0,10,10" />
                                                    </GeometryDrawing.Geometry>
                                                </GeometryDrawing>
                                                <GeometryDrawing Brush="Silver">
                                                    <GeometryDrawing.Geometry>
                                                        <GeometryGroup>
                                                            <RectangleGeometry Rect="0,0,5,5" />
                                                            <RectangleGeometry Rect="5,5,5,5" />
                                                        </GeometryGroup>
                                                    </GeometryDrawing.Geometry>
                                                </GeometryDrawing>
                                            </DrawingGroup>
                                        </DrawingBrush.Drawing>
                                    </DrawingBrush>
                                </Border.Background>
                            </Border>
                            <Border Grid.Row="1"
                                    CornerRadius="0,0,3,3"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="0,1,0,0">
                                <Border.Background>
                                    <LinearGradientBrush EndPoint="1,0.5"
                                                         StartPoint="0,0.5">
                                        <GradientStop x:Name="PART_GradientStart"
                                                      Offset="0"
                                                      Color="#00000000" />
                                        <GradientStop x:Name="PART_GradientEnd"
                                                      Offset="1"
                                                      Color="#FFFFFFFF" />
                                    </LinearGradientBrush>
                                </Border.Background>
                            </Border>
                        </Grid>
    
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled"
                                 Value="false">
                            <Setter Property="Background"
                                    TargetName="root"
                                    Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
                            <Setter Property="Foreground"
                                    Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                        </Trigger>
                        <Trigger SourceName="textBox"
                                 Property="Visibility"
                                 Value="Visible">
                            <Setter TargetName="textBox"
                                    Property="FocusManager.FocusedElement"
                                    Value="{Binding ElementName=textBox}" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>