WPF 實現顏色選擇器控件
控件名:ColorPicker
作 者:WPFDevelopersOrg - 黃佳 | 驚鏵
原文鏈接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers
.NET4 至 .NET6
;Visual Studio 2022
;1)新增 ColorPicker.cs
代碼如下:
包含一個Slider(用于選擇色調)、一個Canvas(用于選擇飽和度和亮度)、一個Thumb(拖動選擇飽和度和亮度的指示器)和一個Button(用于切換顏色類型)。
通過使用ColorPicker控件,用戶可以選擇一個顏色,并且可以通過綁定SelectedColor屬性來獲取所選顏色。這個屬性是一個依賴屬性,支持雙向綁定,并且當顏色發生改變時會觸發SelectedColorChanged事件。
ColorType屬性用于指定顏色類型,可以選擇RGB、HSL或HEX三種類型之一。當用戶點擊Button時,可以循環切換顏色類型。
在控件的模板中,通過TemplatePart特性標記了四個重要的子元素:HueSlider、Canvas、Thumb和Button。在OnApplyTemplate方法中,根據模板找到這些子元素,并注冊相應的事件處理程序。
控件通過使用顏色轉換工具類ColorUtil實現顏色的轉換和計算。當用戶在Canvas上點擊或拖動Thumb時,會計算得到相應的HSB(色調、飽和度、亮度)值,并將其設置為依賴屬性HSB的值。然后根據HSB的值計算出對應的顏色,并更新SelectedColor屬性的值。
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using WPFDevelopers.Utilities;
namespace WPFDevelopers.Controls
{
[TemplatePart(Name=HueSliderColorTemplateName, Type=typeof(Slider))]
[TemplatePart(Name=CanvasTemplateName, Type=typeof(Canvas))]
[TemplatePart(Name=ThumbTemplateName, Type=typeof(Thumb))]
[TemplatePart(Name=ButtonTemplateName, Type=typeof(Button))]
public class ColorPicker : Control
{
private const string HueSliderColorTemplateName="PART_HueSlider";
private const string CanvasTemplateName="PART_Canvas";
private const string ThumbTemplateName="PART_Thumb";
private const string ButtonTemplateName="PART_Button";
private static readonly DependencyPropertyKey HueColorPropertyKey=
DependencyProperty.RegisterReadOnly("HueColor", typeof(Color), typeof(ColorPicker),
new PropertyMetadata(Colors.Red));
public static readonly DependencyProperty HueColorProperty=HueColorPropertyKey.DependencyProperty;
public static readonly DependencyProperty SelectedColorProperty=
DependencyProperty.Register("SelectedColor", typeof(Color), typeof(ColorPicker),
new FrameworkPropertyMetadata(Colors.Red, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnSelectedColorChanged));
private static readonly DependencyPropertyKey HSBPropertyKey=
DependencyProperty.RegisterReadOnly("HSB", typeof(HSB), typeof(ColorPicker),
new PropertyMetadata(new HSB()));
public static readonly DependencyProperty HSBHProperty=HSBPropertyKey.DependencyProperty;
public static readonly DependencyProperty ColorTypeProperty=
DependencyProperty.Register("ColorType", typeof(ColorTypeEnum), typeof(ColorPicker),
new PropertyMetadata(ColorTypeEnum.RGB));
private Button _button;
private Canvas _canvas;
private Slider _hueSliderColor;
private bool _isInnerUpdateSelectedColor;
private Thumb _thumb;
private ColorTypeEnum[] colorTypeEnums;
private int currentGridStateIndex;
static ColorPicker()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorPicker),
new FrameworkPropertyMetadata(typeof(ColorPicker)));
}
public Color HueColor=> (Color) GetValue(HueColorProperty);
public Color SelectedColor
{
get=> (Color) GetValue(SelectedColorProperty);
set=> SetValue(SelectedColorProperty, value);
}
public HSB HSB=> (HSB) GetValue(HSBHProperty);
public ColorTypeEnum ColorType
{
get=> (ColorTypeEnum) GetValue(ColorTypeProperty);
set=> SetValue(ColorTypeProperty, value);
}
private static void OnSelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctrl=d as ColorPicker;
if (ctrl._isInnerUpdateSelectedColor)
{
ctrl._isInnerUpdateSelectedColor=false;
return;
}
var color=(Color) e.NewValue;
double h=0, s=0, b=0;
ColorUtil.HsbFromColor(color, ref h, ref s, ref b);
var hsb=new HSB {H=h, S=s, B=b};
ctrl.SetValue(HueColorPropertyKey, ColorUtil.ColorFromHsb(hsb.H, 1, 1));
ctrl.SetValue(HSBPropertyKey, hsb);
Canvas.SetLeft(ctrl._thumb, s * ctrl._canvas.ActualWidth - ctrl._thumb.ActualWidth / 2);
Canvas.SetTop(ctrl._thumb, (1 - b) * ctrl._canvas.ActualHeight - ctrl._thumb.ActualHeight / 2);
ctrl._hueSliderColor.Value=1 - h;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (_hueSliderColor !=)
_hueSliderColor.ValueChanged -=HueSliderColor_OnValueChanged;
_canvas=GetTemplateChild(CanvasTemplateName) as Canvas;
if (_canvas !=)
{
_canvas.Loaded +=Canvas_Loaded;
_canvas.MouseUp +=Canvas_MouseUp;
}
_thumb=GetTemplateChild(ThumbTemplateName) as Thumb;
if (_thumb !=)
_thumb.DragDelta +=Thumb_DragDelta;
_hueSliderColor=GetTemplateChild(HueSliderColorTemplateName) as Slider;
if (_hueSliderColor !=)
_hueSliderColor.ValueChanged +=HueSliderColor_OnValueChanged;
_button=GetTemplateChild(ButtonTemplateName) as Button;
currentGridStateIndex=0;
colorTypeEnums=(ColorTypeEnum[]) Enum.GetValues(typeof(ColorTypeEnum));
if (_button !=)
_button.Click +=Button_Click;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
currentGridStateIndex=(currentGridStateIndex + 1) % colorTypeEnums.Length;
ColorType=colorTypeEnums[currentGridStateIndex];
}
private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
{
var canvasPosition=e.GetPosition(_canvas);
GetHSB(canvasPosition);
}
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
var canvasPosition=e.GetPosition(_canvas);
GetHSB(canvasPosition);
}
private void GetHSB(Point point, bool isMove=true)
{
var newLeft=point.X - _thumb.ActualWidth / 2;
var newTop=point.Y - _thumb.ActualHeight / 2;
var thumbW=_thumb.ActualWidth / 2;
var thumbH=_thumb.ActualHeight / 2;
var canvasRight=_canvas.ActualWidth - thumbW;
var canvasBottom=_canvas.ActualHeight - thumbH;
if (newLeft < -thumbW)
newLeft=-thumbW;
else if (newLeft > canvasRight)
newLeft=canvasRight;
if (newTop < -thumbH)
newTop=-thumbH;
else if (newTop > canvasBottom)
newTop=canvasBottom;
if (isMove)
{
Canvas.SetLeft(_thumb, newLeft);
Canvas.SetTop(_thumb, newTop);
}
var hsb=new HSB
{
H=HSB.H, S=(newLeft + thumbW) / _canvas.ActualWidth,
B=1 - (newTop + thumbH) / _canvas.ActualHeight
};
SetValue(HSBPropertyKey, hsb);
var currentColor=ColorUtil.ColorFromAhsb(1, HSB.H, HSB.S, HSB.B);
if (SelectedColor !=currentColor)
{
_isInnerUpdateSelectedColor=true;
SelectedColor=currentColor;
}
}
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
var point=Mouse.GetPosition(_canvas);
GetHSB(point);
}
private void Canvas_Loaded(object sender, RoutedEventArgs e)
{
var width=(int) _canvas.ActualWidth;
var height=(int) _canvas.ActualHeight;
var point=new Point(width - _thumb.ActualWidth / 2, -_thumb.ActualHeight / 2);
Canvas.SetLeft(_thumb, point.X);
Canvas.SetTop(_thumb, point.Y);
var hsb=new HSB {H=_hueSliderColor.Value, S=HSB.S, B=HSB.B};
SetValue(HSBPropertyKey, hsb);
}
private void HueSliderColor_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (DoubleUtil.AreClose(HSB.H, e.NewValue))
return;
var hsb=new HSB {H=1 - e.NewValue, S=HSB.S, B=HSB.B};
SetValue(HSBPropertyKey, hsb);
SetValue(HueColorPropertyKey, ColorUtil.ColorFromHsb(HSB.H, 1, 1));
var newLeft=Canvas.GetLeft(_thumb);
var newTop=Canvas.GetTop(_thumb);
var point=new Point(newLeft, newTop);
GetHSB(point, false);
}
}
public enum ColorTypeEnum
{
RGB,
HSL,
HEX
}
}
2)新增 ColorPicker.xaml
代碼如下:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WPFDevelopers.Controls"
xmlns:convert="clr-namespace:WPFDevelopers.Converts"
xmlns:helpers="clr-namespace:WPFDevelopers.Helpers"
xmlns:input="clr-namespace:System.Windows.Input;assembly=PresentationCore"
xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Basic/ControlBasic.xaml" />
</ResourceDictionary.MergedDictionaries>
<convert:ColorToBrushConverter x:Key="WD.ColorToBrushConverter" />
<convert:ColorToRedConverter x:Key="WD.ColorToRedConverter" />
<convert:ColorToGreenConverter x:Key="WD.ColorToGreenConverter" />
<convert:ColorToBlueConverter x:Key="WD.ColorToBlueConverter" />
<convert:ColorTypeToVisibilityConverter x:Key="WD.ColorTypeToVisibilityConverter" />
<convert:ColorToStringConverter x:Key="WD.ColorToStringConverter" />
<convert:HToColorConverter x:Key="WD.HToColorConverter" />
<convert:SToColorConverter x:Key="WD.SToColorConverter" />
<convert:LToColorConverter x:Key="WD.LToColorConverter" />
<LinearGradientBrush x:Key="WD.ColorPickerRainbowBrush" po:Freeze="True">
<GradientStop Color="#FF0000" />
<GradientStop Offset="0.167" Color="#FF00FF" />
<GradientStop Offset="0.334" Color="#0000FF" />
<GradientStop Offset="0.501" Color="#00FFFF" />
<GradientStop Offset="0.668" Color="#00FF00" />
<GradientStop Offset="0.835" Color="#FFFF00" />
<GradientStop Offset="1" Color="#FF0000" />
</LinearGradientBrush>
<ControlTemplate x:Key="WD.ColorPickerSliderThumbTemplate" TargetType="Thumb">
<Border
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Background="Transparent"
BorderBrush="White"
BorderThickness="3"
CornerRadius="{Binding ActualWidth, RelativeSource={RelativeSource Self}, Converter={StaticResource WD.HalfValueConverter}}" />
</ControlTemplate>
<Style x:Key="WD.ColorPickerSliderRepeatButtonBaseStyle" TargetType="RepeatButton">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RepeatButton">
<Rectangle
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="WD.ColorPickerSlider" TargetType="{x:Type Slider}">
<Setter Property="Background" Value="{StaticResource WD.ColorPickerRainbowBrush}" />
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="Orientation" Value="Horizontal" />
<Setter Property="Height" Value="15" />
<Setter Property="Margin" Value="4,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<controls:SmallPanel>
<Border
MaxWidth="{TemplateBinding MaxWidth}"
Margin="{TemplateBinding Margin}"
Background="{TemplateBinding Background}"
CornerRadius="2" />
<Track x:Name="PART_Track" Orientation="{TemplateBinding Orientation}">
<Track.DecreaseRepeatButton>
<RepeatButton Command="{x:Static Slider.DecreaseLarge}" Style="{StaticResource WD.ColorPickerSliderRepeatButtonBaseStyle}" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Command="{x:Static Slider.IncreaseLarge}" Style="{StaticResource WD.ColorPickerSliderRepeatButtonBaseStyle}" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb
x:Name="Thumb"
Width="15"
Height="15"
Focusable="False"
OverridesDefaultStyle="True"
Template="{StaticResource WD.ColorPickerSliderThumbTemplate}">
<Thumb.Effect>
<DropShadowEffect Opacity=".6" ShadowDepth="0" />
</Thumb.Effect>
</Thumb>
</Track.Thumb>
</Track>
</controls:SmallPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style
x:Key="WD.ColorPicker"
BasedOn="{StaticResource WD.ControlBasicStyle}"
TargetType="{x:Type controls:ColorPicker}">
<Setter Property="Width" Value="260" />
<Setter Property="Height" Value="200" />
<Setter Property="Margin" Value="2" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="{DynamicResource WD.BaseSolidColorBrush}" />
<Setter Property="Background" Value="{DynamicResource WD.BackgroundSolidColorBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:ColorPicker}">
<Border
Margin="{TemplateBinding Margin}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Canvas
x:Name="PART_Canvas"
Margin="1,1,1,0"
ClipToBounds="True">
<Canvas.Background>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing>
<GeometryDrawing.Brush>
<LinearGradientBrush EndPoint="1,0">
<GradientStop Offset="0" Color="White" />
<GradientStop Offset="1" Color="{Binding HueColor, RelativeSource={RelativeSource TemplatedParent}}" />
</LinearGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,5,5" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing>
<GeometryDrawing.Brush>
<LinearGradientBrush EndPoint="0,1">
<GradientStop Offset="0" Color="#00000000" />
<GradientStop Offset="1" Color="{StaticResource WD.BlackColor}" />
</LinearGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,5,5" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Canvas.Background>
<Thumb
x:Name="PART_Thumb"
Width="15"
Height="15"
Background="Transparent"
BorderBrush="White"
BorderThickness="3">
<Thumb.Template>
<ControlTemplate TargetType="{x:Type Thumb}">
<controls:SmallPanel>
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{Binding ActualWidth, RelativeSource={RelativeSource Self}, Converter={StaticResource WD.HalfValueConverter}}"
SnapsToDevicePixels="True">
<Border.Effect>
<DropShadowEffect Opacity=".6" ShadowDepth="0" />
</Border.Effect>
</Border>
</controls:SmallPanel>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Canvas>
<Grid Grid.Row="1" Margin="6,5,6,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Ellipse
Width="25"
Height="25"
Margin="0,0,4,0"
Fill="{TemplateBinding SelectedColor,
Converter={StaticResource WD.ColorToBrushConverter}}">
<Ellipse.Effect>
<DropShadowEffect Opacity=".6" ShadowDepth="0" />
</Ellipse.Effect>
</Ellipse>
<Slider
Name="PART_HueSlider"
Grid.Column="1"
Width="Auto"
IsMoveToPointEnabled="True"
LargeChange="0.01"
Maximum="1"
SmallChange="0.01"
Style="{StaticResource WD.ColorPickerSlider}"
Value="1" />
</Grid>
<Grid
Grid.Row="2"
Margin="4"
VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Margin" Value="4,0" />
</Style>
<Style
x:Key="WD.TextBoxColorPicker"
BasedOn="{StaticResource WD.DefaultTextBox}"
TargetType="{x:Type TextBox}">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Width" Value="50" />
</Style>
<Style BasedOn="{StaticResource WD.NumericBox}" TargetType="{x:Type controls:NumericBox}">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Width" Value="50" />
<Setter Property="UpDownButtonsWidth" Value="0" />
<Setter Property="Maximum" Value="255" />
<Setter Property="Minimum" Value="0" />
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Foreground" Value="{DynamicResource WD.PrimaryTextSolidColorBrush}" />
<Setter Property="FontSize" Value="10" />
</Style>
</Grid.Resources>
<Button
Name="PART_Button"
Grid.Column="0"
Width="30"
Height="30"
Margin="0,0,4,0"
helpers:ElementHelper.IsRound="True"
Style="{StaticResource WD.NormalButton}">
<controls:PathIcon Kind="UnfoldMore" />
</Button>
<UniformGrid
Grid.Column="1"
Rows="1"
Visibility="{Binding ColorType, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.ColorTypeToVisibilityConverter}, ConverterParameter={x:Static controls:ColorTypeEnum.RGB}}">
<StackPanel>
<controls:NumericBox Value="{Binding SelectedColor, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.ColorToRedConverter}}" />
<TextBlock Text="R" />
</StackPanel>
<StackPanel>
<controls:NumericBox Value="{Binding SelectedColor, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.ColorToGreenConverter}}" />
<TextBlock Text="G" />
</StackPanel>
<StackPanel>
<controls:NumericBox Value="{Binding SelectedColor, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.ColorToBlueConverter}}" />
<TextBlock Text="B" />
</StackPanel>
</UniformGrid>
<UniformGrid
Grid.Column="1"
Rows="1"
Visibility="{Binding ColorType, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.ColorTypeToVisibilityConverter}, ConverterParameter={x:Static controls:ColorTypeEnum.HSL}}">
<StackPanel>
<TextBox
helpers:TextBoxHelper.AllowOnlyNumericInput="True"
helpers:TextBoxHelper.IsEnterUpdateEnabled="True"
helpers:TextBoxHelper.SelectAllOnClick="True"
Style="{StaticResource WD.TextBoxColorPicker}"
Text="{Binding SelectedColor, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.HToColorConverter}}" />
<TextBlock Text="H" />
</StackPanel>
<StackPanel Grid.Column="1">
<TextBox
helpers:TextBoxHelper.IsEnterUpdateEnabled="True"
helpers:TextBoxHelper.SelectAllOnClick="True"
Style="{StaticResource WD.TextBoxColorPicker}"
Text="{Binding SelectedColor, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.SToColorConverter}}" />
<TextBlock Text="S" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBox
helpers:TextBoxHelper.IsEnterUpdateEnabled="True"
helpers:TextBoxHelper.SelectAllOnClick="True"
Style="{StaticResource WD.TextBoxColorPicker}"
Text="{Binding SelectedColor, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.LToColorConverter}}" />
<TextBlock Text="L" />
</StackPanel>
</UniformGrid>
<StackPanel
Grid.Column="1"
Margin="12,0"
Visibility="{Binding ColorType, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.ColorTypeToVisibilityConverter}, ConverterParameter={x:Static controls:ColorTypeEnum.HEX}}">
<TextBox
helpers:TextBoxHelper.IsEnterUpdateEnabled="True"
helpers:TextBoxHelper.SelectAllOnClick="True"
MaxLength="9"
Text="{Binding SelectedColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource WD.ColorToStringConverter}}"
TextAlignment="Center" />
<TextBlock Text="HEX" />
</StackPanel>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource WD.ColorPicker}" TargetType="{x:Type controls:ColorPicker}" />
</ResourceDictionary>
3)新增顏色轉換工具類 ColorUtil.cs
代碼如下:
using System;
using System.Windows.Media;
using WPFDevelopers.Controls;
namespace WPFDevelopers.Utilities
{
public static class ColorUtil
{
public static Color ConvertHSLToColor(Color color, double h=double.NaN, double sl=double.NaN,
double l=double.NaN)
{
var hsl=RgbToHSL(color);
if (!double.IsNaN(h))
hsl.H=h;
if (!double.IsNaN(sl))
hsl.S=sl;
if (!double.IsNaN(l))
hsl.L=l;
var rgb=HSLToRgb(hsl);
return rgb;
}
private static Color HSLToRgb(HSL hslColor)
{
var rgbColor=new Color();
if (hslColor.S==0)
{
rgbColor.R=(byte) (hslColor.L * 255);
rgbColor.G=(byte) (hslColor.L * 255);
rgbColor.B=(byte) (hslColor.L * 255);
rgbColor.A=(byte) (hslColor.A * 255);
return rgbColor;
}
double t1;
if (hslColor.L < 0.5)
t1=hslColor.L * (1.0 + hslColor.S);
else
t1=hslColor.L + hslColor.S - hslColor.L * hslColor.S;
var t2=2.0 * hslColor.L - t1;
var h=hslColor.H / 360;
var tR=h + 1.0 / 3.0;
var r=SetColor(t1, t2, tR);
var tG=h;
var g=SetColor(t1, t2, tG);
var tB=h - 1.0 / 3.0;
var b=SetColor(t1, t2, tB);
rgbColor.R=(byte) (r * 255);
rgbColor.G=(byte) (g * 255);
rgbColor.B=(byte) (b * 255);
rgbColor.A=(byte) (hslColor.A * 255);
return rgbColor;
}
private static double SetColor(double t1, double t2, double t3)
{
if (t3 < 0) t3 +=1.0;
if (t3 > 1) t3 -=1.0;
double color;
if (6.0 * t3 < 1)
color=t2 + (t1 - t2) * 6.0 * t3;
else if (2.0 * t3 < 1)
color=t1;
else if (3.0 * t3 < 2)
color=t2 + (t1 - t2) * (2.0 / 3.0 - t3) * 6.0;
else
color=t2;
return color;
}
public static HSL RgbToHSL(Color rgbColor)
{
var hslColor=new HSL();
var r=(double) rgbColor.R / 255;
var g=(double) rgbColor.G / 255;
var b=(double) rgbColor.B / 255;
var a=(double) rgbColor.A / 255;
var min=Math.Min(r, Math.Min(g, b));
var max=Math.Max(r, Math.Max(g, b));
var delta=max - min;
if (max==min)
{
hslColor.H=0;
hslColor.S=0;
hslColor.L=max;
return hslColor;
}
hslColor.L=(min + max) / 2;
if (hslColor.L < 0.5)
hslColor.S=delta / (max + min);
else
hslColor.S=delta / (2.0 - max - min);
if (r==max) hslColor.H=(g - b) / delta;
if (g==max) hslColor.H=2.0 + (b - r) / delta;
if (b==max) hslColor.H=4.0 + (r - g) / delta;
hslColor.H *=60;
if (hslColor.H < 0) hslColor.H +=360;
hslColor.A=a;
return hslColor;
}
public static void HsbFromColor(Color C, ref double H, ref double S, ref double B)
{
var r=C.R / 255d;
var g=C.G / 255d;
var b=C.B / 255d;
var max=Math.Max(Math.Max(r, g), b);
var min=Math.Min(Math.Min(r, g), b);
var delta=max - min;
var hue=0d;
var saturation=DoubleUtil.GreaterThan(max, 0) ? delta / max : 0.0;
var brightness=max;
if (!DoubleUtil.IsZero(delta))
{
if (DoubleUtil.AreClose(r, max))
hue=(g - b) / delta;
else if (DoubleUtil.AreClose(g, max))
hue=2 + (b - r) / delta;
else if (DoubleUtil.AreClose(b, max))
hue=4 + (r - g) / delta;
hue=hue * 60;
if (DoubleUtil.LessThan(hue, 0d))
hue +=360;
}
H=hue / 360d;
S=saturation;
B=brightness;
}
public static Color ColorFromAhsb(double a, double h, double s, double b)
{
var r=ColorFromHsb(h, s, b);
r.A=(byte) Math.Round(a * 255d);
return r;
}
public static Color ColorFromHsb(double H, double S, double B)
{
double red=0.0, green=0.0, blue=0.0;
if (DoubleUtil.IsZero(S))
{
red=green=blue=B;
}
else
{
var h=DoubleUtil.IsOne(H) ? 0d : H * 6.0;
var i=(int) Math.Floor(h);
var f=h - i;
var r=B * (1.0 - S);
var s=B * (1.0 - S * f);
var t=B * (1.0 - S * (1.0 - f));
switch (i)
{
case 0:
red=B;
green=t;
blue=r;
break;
case 1:
red=s;
green=B;
blue=r;
break;
case 2:
red=r;
green=B;
blue=t;
break;
case 3:
red=r;
green=s;
blue=B;
break;
case 4:
red=t;
green=r;
blue=B;
break;
case 5:
red=B;
green=r;
blue=s;
break;
}
}
return Color.FromRgb((byte) Math.Round(red * 255.0), (byte) Math.Round(green * 255.0),
(byte) Math.Round(blue * 255.0));
}
}
}
4)示例
代碼如下:
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
<wd:ColorPicker />
原文鏈接: https://github.com/WPFDevelopersOrg/WPFDevelopers
WPF快速入門:構建現代桌面應用程序的指南
WPF(Windows Presentation Foundation)是微軟的一個用于構建桌面客戶端應用程序的UI框架。它提供了一套豐富的控件和工具,使得開發者能夠創建出富有吸引力和高度交互性的用戶界面。本文將帶你快速入門WPF,并通過簡單的示例代碼,讓你了解WPF的基本概念和操作。
在開始WPF開發之前,你需要確保你的開發環境已經配置妥當。你需要安裝Visual Studio,這是微軟的集成開發環境(IDE),它提供了WPF項目模板和必要的工具。
XAML(Extensible Application Markup Language)是WPF使用的標記語言,用于定義用戶界面。在MainWindow.xaml文件中,你可以找到WPF應用程序的主窗口布局。
WPF提供了一系列內置控件,如按鈕、文本框、標簽等。你可以通過XAML來聲明這些控件,并設置它們的屬性。
WPF支持事件驅動編程模型。你可以為控件的事件編寫事件處理程序,以響應用戶的交互。
WPF的數據綁定機制允許你將UI元素與數據源緊密關聯,實現數據的動態更新。
WPF允許你定義資源和樣式,以實現UI的統一和重用。
MVVM(Model-View-ViewModel)是WPF中常用的設計模式,它有助于分離業務邏輯、用戶界面和數據模型。
下面是一個簡單的WPF應用程序示例,它包含一個按鈕和一個文本框。當用戶點擊按鈕時,文本框中的文本將會改變。
MainWindow.xaml
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WPF Demo" Height="200" Width="300">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox x:Name="textBox" Width="200" Height="20" Margin="5"/>
<Button Content="Change Text" Width="80" Height="25" Margin="5" Click="Button_Click"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace WpfApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
textBox.Text="Hello, WPF!";
}
}
}
在上面的示例中,我們創建了一個簡單的WPF應用程序,它包含一個文本框和一個按鈕。在XAML文件中,我們定義了UI布局,并為按鈕的Click事件綁定了一個事件處理程序。在C#代碼后面,我們實現了Button_Click方法,當用戶點擊按鈕時,它會更改文本框中的文本。
本教程提供了一個WPF快速入門的概覽,介紹了WPF的基本概念和操作。通過簡單的示例,你可以開始探索WPF的強大功能,并構建自己的WPF應用程序。隨著實踐的深入,你將能夠利用WPF的高級特性,如數據綁定、樣式和動畫,來創建更加復雜和動態的用戶界面。