优化更新代码,添加界面功能并整合
This commit is contained in:
405
WPFluent/Controls/ColorPickerControl/ColorPickerControl.xaml
Normal file
405
WPFluent/Controls/ColorPickerControl/ColorPickerControl.xaml
Normal file
@@ -0,0 +1,405 @@
|
||||
<ContentControl
|
||||
x:Class="WPFluent.Controls.ColorPickerControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:WPFluent.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<ContentControl.Resources>
|
||||
<Style TargetType="{x:Type Slider}">
|
||||
<Setter Property="MinWidth" Value="104" />
|
||||
<Setter Property="MinHeight" Value="21" />
|
||||
<Setter Property="FocusVisualStyle" Value="{DynamicResource DefaultControlFocusVisualStyle}" />
|
||||
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Slider}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TickBar
|
||||
x:Name="TopTick"
|
||||
Grid.Row="0"
|
||||
Height="6"
|
||||
Fill="Transparent"
|
||||
Placement="Top"
|
||||
SnapsToDevicePixels="True"
|
||||
Visibility="Collapsed" />
|
||||
<Border
|
||||
x:Name="TrackBackground"
|
||||
Grid.Row="1"
|
||||
Height="8"
|
||||
Margin="0"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="4" />
|
||||
<Track x:Name="PART_Track" Grid.Row="1">
|
||||
<Track.DecreaseRepeatButton>
|
||||
<RepeatButton Command="Slider.DecreaseLarge">
|
||||
<RepeatButton.Style>
|
||||
<Style TargetType="{x:Type RepeatButton}">
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type RepeatButton}">
|
||||
<Border Background="Transparent" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</RepeatButton.Style>
|
||||
</RepeatButton>
|
||||
</Track.DecreaseRepeatButton>
|
||||
<Track.Thumb>
|
||||
<Thumb x:Name="Thumb">
|
||||
<Thumb.Style>
|
||||
<Style TargetType="{x:Type Thumb}">
|
||||
<Setter Property="Height" Value="20" />
|
||||
<Setter Property="Width" Value="20" />
|
||||
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ControlElevationBorderBrush}" />
|
||||
<Setter Property="Foreground" Value="Black" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Background" Value="{DynamicResource SliderOuterThumbBackground}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||
<Border
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="16">
|
||||
<Ellipse
|
||||
x:Name="Ellipse"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Fill="{TemplateBinding Foreground}"
|
||||
Stroke="Transparent"
|
||||
StrokeThickness="0" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Thumb.Style>
|
||||
</Thumb>
|
||||
</Track.Thumb>
|
||||
<Track.IncreaseRepeatButton>
|
||||
<RepeatButton Command="Slider.IncreaseLarge">
|
||||
<RepeatButton.Style>
|
||||
<Style TargetType="{x:Type RepeatButton}">
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type RepeatButton}">
|
||||
<Border Background="Transparent" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</RepeatButton.Style>
|
||||
</RepeatButton>
|
||||
|
||||
</Track.IncreaseRepeatButton>
|
||||
</Track>
|
||||
<TickBar
|
||||
x:Name="BottomTick"
|
||||
Grid.Row="2"
|
||||
Height="6"
|
||||
Fill="Transparent"
|
||||
Placement="Bottom"
|
||||
SnapsToDevicePixels="True"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="TickPlacement" Value="TopLeft">
|
||||
<Setter TargetName="TopTick" Property="Visibility" Value="Visible" />
|
||||
</Trigger>
|
||||
<Trigger Property="TickPlacement" Value="BottomRight">
|
||||
<Setter TargetName="BottomTick" Property="Visibility" Value="Visible" />
|
||||
</Trigger>
|
||||
<Trigger Property="TickPlacement" Value="Both">
|
||||
<Setter TargetName="TopTick" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="BottomTick" Property="Visibility" Value="Visible" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True" />
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ColorShadeButtonStyle" TargetType="Button">
|
||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonBackground}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{DynamicResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="MinHeight" Value="20" />
|
||||
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="FocusVisualStyle" Value="{DynamicResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
|
||||
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="False" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Background"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="{TemplateBinding Border.CornerRadius}"
|
||||
SnapsToDevicePixels="True">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Focusable="False"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True" />
|
||||
<Condition Property="IsMouseCaptureWithin" Value="False" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="Background" Property="Opacity" Value="0.8" />
|
||||
</MultiTrigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="Background" Property="Opacity" Value="0.9" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ContentControl.Resources>
|
||||
|
||||
<Border
|
||||
BorderBrush="{DynamicResource SurfaceStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{DynamicResource ControlCornerRadius}">
|
||||
<Grid
|
||||
x:Name="PickerPanel"
|
||||
Width="248"
|
||||
VerticalAlignment="Stretch"
|
||||
ClipToBounds="True"
|
||||
Opacity="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="36" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<controls:Button
|
||||
x:Name="ColorVariation1Button"
|
||||
Grid.Column="0"
|
||||
Background="LightPink"
|
||||
Click="OnColorVariationButtonClicked"
|
||||
CornerRadius="4,0,0,4"
|
||||
Style="{DynamicResource ColorShadeButtonStyle}"
|
||||
ToolTipService.ToolTip="Select color" />
|
||||
<Button
|
||||
x:Name="ColorVariation2Button"
|
||||
Grid.Column="1"
|
||||
Background="LightPink"
|
||||
Click="OnColorVariationButtonClicked"
|
||||
Style="{DynamicResource ColorShadeButtonStyle}"
|
||||
ToolTipService.ToolTip="Select color" />
|
||||
<Button
|
||||
x:Name="ColorVariation3Button"
|
||||
Grid.Column="3"
|
||||
Background="LightPink"
|
||||
Click="OnColorVariationButtonClicked"
|
||||
Style="{DynamicResource ColorShadeButtonStyle}"
|
||||
ToolTipService.ToolTip="Select color" />
|
||||
<controls:Button
|
||||
x:Name="ColorVariation4Button"
|
||||
Grid.Column="4"
|
||||
Background="LightPink"
|
||||
Click="OnColorVariationButtonClicked"
|
||||
CornerRadius="0,4,4,0"
|
||||
Style="{DynamicResource ColorShadeButtonStyle}"
|
||||
ToolTipService.ToolTip="Select color" />
|
||||
<Button
|
||||
x:Name="CurrentColorButton"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="5"
|
||||
Width="104"
|
||||
Margin="72,0"
|
||||
Click="OnCurrentColorButtonClicked"
|
||||
Style="{DynamicResource ColorShadeButtonStyle}"
|
||||
ToolTipService.ToolTip="Adjust color">
|
||||
<controls:Flyout
|
||||
x:Name="DetailsFlyout"
|
||||
Margin="160,0,0,0"
|
||||
Closed="OnDetailsFlyoutClosed"
|
||||
Placement="Right">
|
||||
<Grid x:Name="DetailsGrid" KeyboardNavigation.TabNavigation="Contained">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="44" />
|
||||
<ColumnDefinition Width="86" />
|
||||
<ColumnDefinition Width="86" />
|
||||
<ColumnDefinition Width="86" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="8" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="8" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="12" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="12" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="H"
|
||||
TextAlignment="Center" />
|
||||
<Slider
|
||||
x:Name="HueGradientSlider"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="3"
|
||||
VerticalAlignment="Center"
|
||||
IsMoveToPointEnabled="True"
|
||||
Maximum="289"
|
||||
Minimum="0"
|
||||
ValueChanged="OnHueGradientSliderValueChanged" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Text="S"
|
||||
TextAlignment="Center" />
|
||||
<Slider
|
||||
x:Name="SaturationGradientSlider"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="3"
|
||||
IsMoveToPointEnabled="True"
|
||||
Maximum="289"
|
||||
Minimum="0"
|
||||
ValueChanged="OnSaturationGradientSliderValueChanged">
|
||||
<Slider.Background>
|
||||
<LinearGradientBrush StartPoint="0, 0.5" EndPoint="1,0.5">
|
||||
<GradientStop x:Name="SaturationStartColor" Color="Black" />
|
||||
<GradientStop x:Name="SaturationStopColor" Offset="1" Color="Red" />
|
||||
</LinearGradientBrush>
|
||||
</Slider.Background>
|
||||
</Slider>
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Text="V"
|
||||
TextAlignment="Center" />
|
||||
<Slider
|
||||
x:Name="ValueGradientSlider"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="3"
|
||||
IsMoveToPointEnabled="True"
|
||||
Maximum="289"
|
||||
Minimum="0"
|
||||
ValueChanged="OnValueGradientSliderValueChanged">
|
||||
<Slider.Background>
|
||||
<LinearGradientBrush StartPoint="0, 0.5" EndPoint="1,0.5">
|
||||
<GradientStop x:Name="ValueStartColor" Color="Black" />
|
||||
<GradientStop x:Name="ValueStopColor" Offset="1" Color="Red" />
|
||||
</LinearGradientBrush>
|
||||
</Slider.Background>
|
||||
</Slider>
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Text="RGB"
|
||||
TextAlignment="Center" />
|
||||
<controls:NumberBox
|
||||
x:Name="RNumberBox"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Maximum="255"
|
||||
Minimum="0"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
TextChanged="OnRgbNumberBoxTextChanged" />
|
||||
<controls:NumberBox
|
||||
x:Name="GNumberBox"
|
||||
Grid.Row="6"
|
||||
Grid.Column="2"
|
||||
Margin="4,0,0,0"
|
||||
Maximum="255"
|
||||
Minimum="0"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
TextChanged="OnRgbNumberBoxTextChanged" />
|
||||
<controls:NumberBox
|
||||
x:Name="BNumberBox"
|
||||
Grid.Row="6"
|
||||
Grid.Column="3"
|
||||
Margin="4,0,0,0"
|
||||
Maximum="255"
|
||||
Minimum="0"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
TextChanged="OnRgbNumberBoxTextChanged" />
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
Width="38"
|
||||
VerticalAlignment="Center"
|
||||
Text="HEX"
|
||||
TextAlignment="Center" />
|
||||
<TextBox
|
||||
x:Name="HexCode"
|
||||
Grid.Row="8"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="3"
|
||||
HorizontalAlignment="Stretch"
|
||||
CharacterCasing="Lower"
|
||||
GotKeyboardFocus="OnHexCodeGotKeyboardFocus"
|
||||
MaxLength="7"
|
||||
TextChanged="OnHexCodeTextChanged"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:Button
|
||||
x:Name="OkButton"
|
||||
Grid.Row="9"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="4"
|
||||
Margin="0,32,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Appearance="Primary"
|
||||
Click="OnOkButtonClicked"
|
||||
Content="Select" />
|
||||
</Grid>
|
||||
</controls:Flyout>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ContentControl>
|
||||
405
WPFluent/Controls/ColorPickerControl/ColorPickerControl.xaml.cs
Normal file
405
WPFluent/Controls/ColorPickerControl/ColorPickerControl.xaml.cs
Normal file
@@ -0,0 +1,405 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
|
||||
using Color = System.Windows.Media.Color;
|
||||
using Point = System.Windows.Point;
|
||||
using TextBox = System.Windows.Controls.TextBox;
|
||||
|
||||
namespace WPFluent.Controls;
|
||||
|
||||
public sealed partial class ColorPickerControl
|
||||
{
|
||||
private double _currH = 360;
|
||||
private double _currS = 1;
|
||||
private double _currV = 1;
|
||||
private bool _ignoreHexChanges;
|
||||
private bool _ignoreRgbChanges;
|
||||
private bool _ignoreGradientsChanges;
|
||||
private bool _isCollapsed = true;
|
||||
private Color _originalColor;
|
||||
private Color _currentColor;
|
||||
|
||||
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register(nameof(SelectedColor),
|
||||
typeof(Color),
|
||||
typeof(ColorPickerControl),
|
||||
new FrameworkPropertyMetadata(Color.FromArgb(0, 0, 0, 0), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedColorPropertyChanged));
|
||||
|
||||
public ColorPickerControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
UpdateHueGradient(1, 1);
|
||||
}
|
||||
|
||||
public Color SelectedColor
|
||||
{
|
||||
get => (Color)GetValue(SelectedColorProperty);
|
||||
set => SetValue(SelectedColorProperty, value);
|
||||
}
|
||||
|
||||
private static void SelectedColorPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = (ColorPickerControl)dependencyObject;
|
||||
var newColor = (Color)e.NewValue;
|
||||
|
||||
control._originalColor = control._currentColor = newColor;
|
||||
var newColorBackground = new SolidColorBrush(newColor);
|
||||
control.CurrentColorButton.Background = newColorBackground;
|
||||
|
||||
control._ignoreHexChanges = true;
|
||||
control._ignoreRgbChanges = true;
|
||||
|
||||
control.HexCode.Text = ColorToHex(newColor);
|
||||
control.RNumberBox.Value = newColor.R;
|
||||
control.GNumberBox.Value = newColor.G;
|
||||
control.BNumberBox.Value = newColor.B;
|
||||
control.SetColorFromTextBoxes(System.Drawing.Color.FromArgb(newColor.R, newColor.G, newColor.B));
|
||||
|
||||
control._ignoreRgbChanges = false;
|
||||
control._ignoreHexChanges = false;
|
||||
|
||||
var hsv = ColorFormatUtils.ConvertToHsvColor(System.Drawing.Color.FromArgb(newColor.R, newColor.G, newColor.B));
|
||||
SetColorVariationsForCurrentColor(dependencyObject, hsv);
|
||||
}
|
||||
|
||||
private void UpdateHueGradient(double saturation, double value)
|
||||
{
|
||||
var g6 = HsvColor.HueSpectrum(saturation, value);
|
||||
|
||||
var gradientBrush = new LinearGradientBrush
|
||||
{
|
||||
StartPoint = new Point(0, 0),
|
||||
EndPoint = new Point(1, 0)
|
||||
};
|
||||
|
||||
for (var i = 0; i < g6.Length; i++)
|
||||
{
|
||||
var stop = new GradientStop(g6[i], i * 0.16);
|
||||
gradientBrush.GradientStops.Add(stop);
|
||||
}
|
||||
|
||||
HueGradientSlider.Background = gradientBrush;
|
||||
}
|
||||
|
||||
private static void SetColorVariationsForCurrentColor(DependencyObject d, (double Hue, double Saturation, double Value) hsv)
|
||||
{
|
||||
var hueCoefficient = 0;
|
||||
var hueCoefficient2 = 0;
|
||||
if (1 - hsv.Value < 0.15)
|
||||
{
|
||||
hueCoefficient = 1;
|
||||
}
|
||||
|
||||
if (hsv.Value - 0.3 < 0)
|
||||
{
|
||||
hueCoefficient2 = 1;
|
||||
}
|
||||
|
||||
var s = hsv.Saturation;
|
||||
var control = (ColorPickerControl)d;
|
||||
|
||||
control.ColorVariation1Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Min(hsv.Hue + hueCoefficient * 8, 360), s, Math.Min(hsv.Value + 0.3, 1)));
|
||||
control.ColorVariation2Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Min(hsv.Hue + hueCoefficient * 4, 360), s, Math.Min(hsv.Value + 0.15, 1)));
|
||||
|
||||
control.ColorVariation3Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Max(hsv.Hue - hueCoefficient2 * 4, 0), s, Math.Max(hsv.Value - 0.2, 0)));
|
||||
control.ColorVariation4Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Max(hsv.Hue - hueCoefficient2 * 8, 0), s, Math.Max(hsv.Value - 0.3, 0)));
|
||||
}
|
||||
|
||||
private void UpdateValueColorGradient(double posX)
|
||||
{
|
||||
ValueGradientSlider.Value = posX;
|
||||
|
||||
_currV = posX / ValueGradientSlider.Maximum;
|
||||
|
||||
UpdateHueGradient(_currS, _currV);
|
||||
|
||||
SaturationStartColor.Color = HsvColor.RgbFromHsv(_currH, 0f, _currV);
|
||||
SaturationStopColor.Color = HsvColor.RgbFromHsv(_currH, 1f, _currV);
|
||||
}
|
||||
|
||||
private void UpdateSaturationColorGradient(double posX)
|
||||
{
|
||||
SaturationGradientSlider.Value = posX;
|
||||
|
||||
_currS = posX / HueGradientSlider.Maximum;
|
||||
|
||||
UpdateHueGradient(_currS, _currV);
|
||||
|
||||
ValueStartColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 0f);
|
||||
ValueStopColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 1f);
|
||||
}
|
||||
|
||||
private void UpdateHueColorGradient(double posX)
|
||||
{
|
||||
HueGradientSlider.Value = posX;
|
||||
_currH = posX / HueGradientSlider.Maximum * 360;
|
||||
|
||||
SaturationStartColor.Color = HsvColor.RgbFromHsv(_currH, 0f, _currV);
|
||||
SaturationStopColor.Color = HsvColor.RgbFromHsv(_currH, 1f, _currV);
|
||||
|
||||
ValueStartColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 0f);
|
||||
ValueStopColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 1f);
|
||||
}
|
||||
|
||||
private void UpdateTextBoxesAndCurrentColor(Color currentColor)
|
||||
{
|
||||
if (!_ignoreHexChanges)
|
||||
{
|
||||
// Second parameter is set to keep the hashtag if typed by the user before
|
||||
HexCode.Text = ColorToHex(currentColor, HexCode.Text);
|
||||
}
|
||||
|
||||
if (!_ignoreRgbChanges)
|
||||
{
|
||||
RNumberBox.Value = currentColor.R;
|
||||
GNumberBox.Value = currentColor.G;
|
||||
BNumberBox.Value = currentColor.B;
|
||||
}
|
||||
|
||||
_currentColor = currentColor;
|
||||
CurrentColorButton.Background = new SolidColorBrush(currentColor);
|
||||
}
|
||||
|
||||
private void OnCurrentColorButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowDetails();
|
||||
}
|
||||
|
||||
private void ShowDetails()
|
||||
{
|
||||
if (_isCollapsed)
|
||||
{
|
||||
_isCollapsed = false;
|
||||
|
||||
var resizeColor = new DoubleAnimation(256, new Duration(TimeSpan.FromMilliseconds(250)))
|
||||
{
|
||||
EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseInOut }
|
||||
};
|
||||
|
||||
var moveColor = new ThicknessAnimation(new Thickness(0), new Duration(TimeSpan.FromMilliseconds(250)))
|
||||
{
|
||||
EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseInOut }
|
||||
};
|
||||
|
||||
CurrentColorButton.BeginAnimation(WidthProperty, resizeColor);
|
||||
CurrentColorButton.BeginAnimation(MarginProperty, moveColor);
|
||||
CurrentColorButton.IsEnabled = false;
|
||||
DetailsFlyout.IsOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void HideDetails()
|
||||
{
|
||||
if (_isCollapsed) return;
|
||||
|
||||
_isCollapsed = true;
|
||||
|
||||
var resizeColor = new DoubleAnimation(165, new Duration(TimeSpan.FromMilliseconds(150)))
|
||||
{
|
||||
EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseInOut }
|
||||
};
|
||||
|
||||
var moveColor = new ThicknessAnimation(new Thickness(72, 0, 72, 0), new Duration(TimeSpan.FromMilliseconds(150)))
|
||||
{
|
||||
EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseInOut }
|
||||
};
|
||||
|
||||
CurrentColorButton.BeginAnimation(WidthProperty, resizeColor);
|
||||
CurrentColorButton.BeginAnimation(MarginProperty, moveColor);
|
||||
CurrentColorButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
private void OnOkButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SelectedColor = _currentColor;
|
||||
DetailsFlyout.Hide();
|
||||
}
|
||||
|
||||
private void OnDetailsFlyoutClosed(object sender, object e)
|
||||
{
|
||||
HideDetails();
|
||||
|
||||
// Revert to original color
|
||||
var originalColorBackground = new SolidColorBrush(_originalColor);
|
||||
CurrentColorButton.Background = originalColorBackground;
|
||||
|
||||
HexCode.Text = ColorToHex(_originalColor);
|
||||
}
|
||||
|
||||
private void OnColorVariationButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectedColor = ((SolidColorBrush)((System.Windows.Controls.Button)sender).Background).Color;
|
||||
SelectedColor = selectedColor;
|
||||
}
|
||||
|
||||
private void OnSaturationGradientSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
||||
{
|
||||
UpdateSaturationColorGradient(((Slider)sender).Value);
|
||||
_ignoreGradientsChanges = true;
|
||||
UpdateTextBoxesAndCurrentColor(HsvColor.RgbFromHsv(_currH, _currS, _currV));
|
||||
_ignoreGradientsChanges = false;
|
||||
}
|
||||
|
||||
private void OnHueGradientSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
||||
{
|
||||
UpdateHueColorGradient(((Slider)sender).Value);
|
||||
_ignoreGradientsChanges = true;
|
||||
UpdateTextBoxesAndCurrentColor(HsvColor.RgbFromHsv(_currH, _currS, _currV));
|
||||
_ignoreGradientsChanges = false;
|
||||
}
|
||||
|
||||
private void OnValueGradientSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
||||
{
|
||||
UpdateValueColorGradient(((Slider)sender).Value);
|
||||
_ignoreGradientsChanges = true;
|
||||
UpdateTextBoxesAndCurrentColor(HsvColor.RgbFromHsv(_currH, _currS, _currV));
|
||||
_ignoreGradientsChanges = false;
|
||||
}
|
||||
|
||||
private void OnHexCodeTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
var newValue = ((TextBox)sender).Text;
|
||||
|
||||
// support hex with 3 and 6 characters and optional with hashtag
|
||||
var reg = new Regex("^#?([0-9A-Fa-f]{3}){1,2}$");
|
||||
|
||||
if (!reg.IsMatch(newValue))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_ignoreHexChanges) return;
|
||||
|
||||
var converter = new System.Drawing.ColorConverter();
|
||||
|
||||
// "FormatHexColorString()" is needed to add hashtag if missing and to convert the hex code from three to six characters. Without this we get format exceptions and incorrect color values.
|
||||
var color = (System.Drawing.Color)converter.ConvertFromString(FormatHexColorString(HexCode.Text))!;
|
||||
|
||||
_ignoreHexChanges = true;
|
||||
SetColorFromTextBoxes(color);
|
||||
_ignoreHexChanges = false;
|
||||
}
|
||||
|
||||
private void SetColorFromTextBoxes(System.Drawing.Color color)
|
||||
{
|
||||
if (!_ignoreGradientsChanges)
|
||||
{
|
||||
var hsv = ColorFormatUtils.ConvertToHsvColor(color);
|
||||
|
||||
var huePosition = hsv.Hue / 360 * HueGradientSlider.Maximum;
|
||||
var saturationPosition = hsv.Saturation * SaturationGradientSlider.Maximum;
|
||||
var valuePosition = hsv.Value * ValueGradientSlider.Maximum;
|
||||
UpdateHueColorGradient(huePosition);
|
||||
UpdateSaturationColorGradient(saturationPosition);
|
||||
UpdateValueColorGradient(valuePosition);
|
||||
}
|
||||
|
||||
UpdateTextBoxesAndCurrentColor(Color.FromRgb(color.R, color.G, color.B));
|
||||
}
|
||||
|
||||
private static string ColorToHex(Color color, string oldValue = "")
|
||||
{
|
||||
#if NETCOREAPP
|
||||
var newHexString = BitConverter.ToString([color.R, color.G, color.B]).Replace("-", string.Empty, StringComparison.InvariantCulture);
|
||||
#else
|
||||
var newHexString = BitConverter.ToString([color.R, color.G, color.B]).Replace("-", string.Empty);
|
||||
#endif
|
||||
newHexString = newHexString.ToLowerInvariant();
|
||||
|
||||
// Return only with hashtag if user typed it before
|
||||
#if NETCOREAPP
|
||||
var addHashtag = oldValue.StartsWith('#');
|
||||
#else
|
||||
var addHashtag = oldValue.StartsWith("#");
|
||||
#endif
|
||||
return addHashtag ? "#" + newHexString : newHexString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats the hex code string to be accepted by <see cref="System.Drawing.ColorConverter.ConvertFromString(string)"/>. We are adding hashtag at the beginning if needed and convert from three characters to six characters code.
|
||||
/// </summary>
|
||||
/// <param name="hexCodeText">The string we read from the hex text box.</param>
|
||||
/// <returns>Formatted string with hashtag and six characters of hex code.</returns>
|
||||
private static string FormatHexColorString(string hexCodeText)
|
||||
{
|
||||
if (hexCodeText.Length is 3 or 4)
|
||||
{
|
||||
// Hex with or without hashTag and three characters
|
||||
return Regex.Replace(hexCodeText, "^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$", "#$1$1$2$2$3$3");
|
||||
}
|
||||
|
||||
// Hex with or without hashTag and six characters
|
||||
#if NETCOREAPP
|
||||
return hexCodeText.StartsWith('#') ? hexCodeText : "#" + hexCodeText;
|
||||
#else
|
||||
return hexCodeText.StartsWith("#") ? hexCodeText : "#" + hexCodeText;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnHexCodeGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
|
||||
{
|
||||
((TextBox)sender).SelectAll();
|
||||
}
|
||||
|
||||
private void OnRgbNumberBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (_ignoreRgbChanges) return;
|
||||
|
||||
var numberBox = (NumberBox)sender;
|
||||
|
||||
if (!RNumberBox.Value.HasValue) return;
|
||||
if (!GNumberBox.Value.HasValue) return;
|
||||
if (!BNumberBox.Value.HasValue) return;
|
||||
|
||||
var r = numberBox.Name == "RNumberBox" ? GetValueFromNumberBox(numberBox) : (byte)RNumberBox.Value;
|
||||
var g = numberBox.Name == "GNumberBox" ? GetValueFromNumberBox(numberBox) : (byte)GNumberBox.Value;
|
||||
var b = numberBox.Name == "BNumberBox" ? GetValueFromNumberBox(numberBox) : (byte)BNumberBox.Value;
|
||||
|
||||
_ignoreRgbChanges = true;
|
||||
SetColorFromTextBoxes(System.Drawing.Color.FromArgb(r, g, b));
|
||||
_ignoreRgbChanges = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NumberBox provides value only after it has been validated - happens after pressing enter or leaving this control.
|
||||
/// However, we need to get value immediately after the underlying textbox value changes
|
||||
/// </summary>
|
||||
/// <param name="numberBox">numberBox control which value we want to get</param>
|
||||
/// <returns>Validated value as per numberbox conditions, if content is invalid it returns previous value</returns>
|
||||
private static byte GetValueFromNumberBox(NumberBox numberBox)
|
||||
{
|
||||
if (!numberBox.Value.HasValue) return byte.MinValue;
|
||||
|
||||
var parsedValue = ParseDouble(numberBox.Text);
|
||||
if (!parsedValue.HasValue) return (byte)numberBox.Value;
|
||||
|
||||
var parsedValueByte = (byte)parsedValue;
|
||||
|
||||
if (parsedValueByte >= numberBox.Minimum && parsedValueByte <= numberBox.Maximum)
|
||||
{
|
||||
return parsedValueByte;
|
||||
}
|
||||
|
||||
// not valid input, return previous value
|
||||
return (byte)numberBox.Value;
|
||||
}
|
||||
|
||||
public static double? ParseDouble(string text)
|
||||
{
|
||||
if (double.TryParse(text, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
83
WPFluent/Controls/ColorPickerControl/HSVColor.cs
Normal file
83
WPFluent/Controls/ColorPickerControl/HSVColor.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
using Color = System.Windows.Media.Color;
|
||||
|
||||
namespace WPFluent.Controls;
|
||||
|
||||
public static class HsvColor
|
||||
{
|
||||
public static Color[] GetSpectrum()
|
||||
{
|
||||
var rgbs = new Color[360];
|
||||
|
||||
for(var h = 0; h < 360; h++)
|
||||
{
|
||||
rgbs[h] = RgbFromHsv(h, 1f, 1f);
|
||||
}
|
||||
|
||||
return rgbs;
|
||||
}
|
||||
|
||||
public static Color[] HueSpectrum(double saturation, double value)
|
||||
{
|
||||
var rgbs = new Color[7];
|
||||
|
||||
for(var h = 0; h < 7; h++)
|
||||
{
|
||||
rgbs[h] = RgbFromHsv(h * 60, saturation, value);
|
||||
}
|
||||
|
||||
return rgbs;
|
||||
}
|
||||
|
||||
public static Color RgbFromHsv(double h, double s, double v)
|
||||
{
|
||||
if(h > 360 || h < 0 || s > 1 || s < 0 || v > 1 || v < 0)
|
||||
{
|
||||
return Color.FromRgb(0, 0, 0);
|
||||
}
|
||||
|
||||
var c = v * s;
|
||||
var x = c * (1 - Math.Abs(((h / 60) % 2) - 1));
|
||||
var m = v - c;
|
||||
|
||||
double r = 0, g = 0, b = 0;
|
||||
|
||||
if(h < 60)
|
||||
{
|
||||
r = c;
|
||||
g = x;
|
||||
}
|
||||
else if(h < 120)
|
||||
{
|
||||
r = x;
|
||||
g = c;
|
||||
}
|
||||
else if(h < 180)
|
||||
{
|
||||
g = c;
|
||||
b = x;
|
||||
}
|
||||
else if(h < 240)
|
||||
{
|
||||
g = x;
|
||||
b = c;
|
||||
}
|
||||
else if(h < 300)
|
||||
{
|
||||
r = x;
|
||||
b = c;
|
||||
}
|
||||
else if(h <= 360)
|
||||
{
|
||||
r = c;
|
||||
b = x;
|
||||
}
|
||||
|
||||
return Color.FromRgb((byte)((r + m) * 255), (byte)((g + m) * 255), (byte)((b + m) * 255));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user