diff --git a/src/WPFDevelopers.Shared/Controls/ColorPicker/ColorPicker.cs b/src/WPFDevelopers.Shared/Controls/ColorPicker/ColorPicker.cs new file mode 100644 index 00000000..be124233 --- /dev/null +++ b/src/WPFDevelopers.Shared/Controls/ColorPicker/ColorPicker.cs @@ -0,0 +1,220 @@ +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 Slider _hueSliderColor; + + private Canvas _canvas; + + private Thumb _thumb; + + private Button _button; + + private bool _isInnerUpdateSelectedColor = false; + + private int currentGridStateIndex; + + private ColorTypeEnum[] colorTypeEnums; + + private static readonly DependencyPropertyKey HueColorPropertyKey = + DependencyProperty.RegisterReadOnly("HueColor", typeof(Color), typeof(ColorPicker), new PropertyMetadata(Colors.Red)); + public static readonly DependencyProperty HueColorProperty = HueColorPropertyKey.DependencyProperty; + public Color HueColor + { + get { return (Color)GetValue(HueColorProperty); } + } + + public Color SelectedColor + { + get { return (Color)GetValue(SelectedColorProperty); } + set { SetValue(SelectedColorProperty, value); } + } + + public static readonly DependencyProperty SelectedColorProperty = + DependencyProperty.Register("SelectedColor", typeof(Color), typeof(ColorPicker), new FrameworkPropertyMetadata(Colors.Red, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedColorChanged)); + + 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; + } + + private static readonly DependencyPropertyKey HSBPropertyKey = + DependencyProperty.RegisterReadOnly("HSB", typeof(HSB), typeof(ColorPicker), new PropertyMetadata(new HSB())); + + public static readonly DependencyProperty HSBHProperty = HSBPropertyKey.DependencyProperty; + + public HSB HSB + { + get { return (HSB)GetValue(HSBHProperty); } + } + + + + public ColorTypeEnum ColorType + { + get { return (ColorTypeEnum)GetValue(ColorTypeProperty); } + set { SetValue(ColorTypeProperty, value); } + } + + public static readonly DependencyProperty ColorTypeProperty = + DependencyProperty.Register("ColorType", typeof(ColorTypeEnum), typeof(ColorPicker), new PropertyMetadata(ColorTypeEnum.RGB)); + + + + static ColorPicker() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorPicker), new FrameworkPropertyMetadata(typeof(ColorPicker))); + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + if (_hueSliderColor != null) + _hueSliderColor.ValueChanged -= HueSliderColor_OnValueChanged; + _canvas = GetTemplateChild(CanvasTemplateName) as Canvas; + if (_canvas != null) + { + + _canvas.Loaded += Canvas_Loaded; + _canvas.MouseUp += _canvas_MouseUp; + } + _thumb = GetTemplateChild(ThumbTemplateName) as Thumb; + if (_thumb != null) + _thumb.DragDelta += Thumb_DragDelta; + _hueSliderColor = GetTemplateChild(HueSliderColorTemplateName) as Slider; + if (_hueSliderColor != null) + _hueSliderColor.ValueChanged += HueSliderColor_OnValueChanged; + + _button = GetTemplateChild(ButtonTemplateName) as Button; + currentGridStateIndex = 0; + colorTypeEnums = (ColorTypeEnum[])Enum.GetValues(typeof(ColorTypeEnum)); + if (_button != null) + _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); + } + + 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 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, + } +} diff --git a/src/WPFDevelopers.Shared/Controls/ColorPicker/HSB.cs b/src/WPFDevelopers.Shared/Controls/ColorPicker/HSB.cs new file mode 100644 index 00000000..b4c9618a --- /dev/null +++ b/src/WPFDevelopers.Shared/Controls/ColorPicker/HSB.cs @@ -0,0 +1,9 @@ +namespace WPFDevelopers.Controls +{ + public struct HSB + { + public double H { get; set; } + public double S { get; set; } + public double B { get; set; } + } +} diff --git a/src/WPFDevelopers.Shared/Core/Converts/ColorConverter.cs b/src/WPFDevelopers.Shared/Core/Converts/ColorConverter.cs new file mode 100644 index 00000000..d46b6927 --- /dev/null +++ b/src/WPFDevelopers.Shared/Core/Converts/ColorConverter.cs @@ -0,0 +1,151 @@ +using System; +using System.Globalization; +using System.Text.RegularExpressions; +using System.Windows.Data; +using System.Windows.Media; +using WPFDevelopers.Utilities; + +namespace WPFDevelopers.Converts +{ + public class ColorToRedConverter : IValueConverter + { + private Color? _curColor = null; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + _curColor = (Color)value; + + return _curColor.Value.R; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return Color.FromArgb(_curColor.Value.A, (byte)(double.Parse(value.ToString())), _curColor.Value.G, _curColor.Value.B); + } + } + + public class ColorToGreenConverter : IValueConverter + { + private Color? _curColor = null; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + _curColor = (Color)value; + + return _curColor.Value.G; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return Color.FromArgb(_curColor.Value.A, _curColor.Value.R, (byte)(double.Parse(value.ToString())), _curColor.Value.B); + } + } + + public class ColorToBlueConverter : IValueConverter + { + private Color? _curColor = null; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + _curColor = (Color)value; + + return _curColor.Value.B; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return Color.FromArgb(_curColor.Value.A, _curColor.Value.R, _curColor.Value.G, (byte)(double.Parse(value.ToString()))); + } + } + + public class ColorToBrushConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return new SolidColorBrush((Color)value); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + + public class ColorToStringConverter : IValueConverter + { + private Color? _curColor = null; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + _curColor = (Color)value; + + return _curColor.ToString(); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + var colorStr = (string)value; + + if (!string.IsNullOrWhiteSpace(colorStr) && Regex.IsMatch(colorStr, @"^#[\da-fA-F]{6,8}$")) + return ColorConverter.ConvertFromString(colorStr); + + return _curColor.Value; + } + + } + + public class HToColorConverter : IValueConverter + { + private Color? _curColor = null; + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + _curColor = (Color)value; + return $"{ColorUtil.ColorFromH(_curColor.Value)}"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + var colorStr = (string)value; + if (!string.IsNullOrWhiteSpace(colorStr) && double.TryParse(colorStr, out double hValue)) + _curColor = ColorUtil.ConvertHSLToColor(_curColor.Value, hValue: hValue % 360); + return _curColor; + } + } + + public class SToColorConverter : IValueConverter + { + private Color? _curColor = null; + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + _curColor = (Color)value; + return $"{ColorUtil.ColorFromS(_curColor.Value)}%"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + var colorStr = (string)value; + if (!string.IsNullOrWhiteSpace(colorStr) && double.TryParse(colorStr, out double sValue)) + _curColor = ColorUtil.ConvertHSLToColor(_curColor.Value, sValue: sValue / 100); + return _curColor; + } + } + + public class LToColorConverter : IValueConverter + { + private Color? _curColor = null; + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + _curColor = (Color)value; + return $"{ColorUtil.ColorFromL(_curColor.Value)}%"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + var colorStr = (string)value; + if (!string.IsNullOrWhiteSpace(colorStr) && double.TryParse(colorStr, out double lValue)) + _curColor = ColorUtil.ConvertHSLToColor(_curColor.Value, lValue: lValue / 100); + return _curColor; + } + } + +} diff --git a/src/WPFDevelopers.Shared/Core/Converts/ColorTypeToVisibilityConverter.cs b/src/WPFDevelopers.Shared/Core/Converts/ColorTypeToVisibilityConverter.cs new file mode 100644 index 00000000..f045d882 --- /dev/null +++ b/src/WPFDevelopers.Shared/Core/Converts/ColorTypeToVisibilityConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace WPFDevelopers.Converts +{ + public class ColorTypeToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is Enum enumValue && parameter is Enum targetEnumValue) + { + if (enumValue.Equals(targetEnumValue)) + return Visibility.Visible; + } + return Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/WPFDevelopers.Shared/Core/Helpers/TextBoxHelper.cs b/src/WPFDevelopers.Shared/Core/Helpers/TextBoxHelper.cs new file mode 100644 index 00000000..72f84930 --- /dev/null +++ b/src/WPFDevelopers.Shared/Core/Helpers/TextBoxHelper.cs @@ -0,0 +1,126 @@ +using System; +using System.Linq; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace WPFDevelopers.Helpers +{ + public static class TextBoxHelper + { + public static readonly DependencyProperty SelectAllOnClickProperty = + DependencyProperty.RegisterAttached("SelectAllOnClick", typeof(bool), typeof(TextBoxHelper), new PropertyMetadata(false, OnSelectAllOnClickChanged)); + + private static void OnSelectAllOnClickChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is TextBox textBox) + { + if ((bool)e.NewValue) + textBox.PreviewMouseLeftButtonDown += TextBox_PreviewMouseLeftButtonDown; + else + textBox.PreviewMouseLeftButtonDown -= TextBox_PreviewMouseLeftButtonDown; + } + } + + + public static readonly DependencyProperty AllowOnlyNumericInputProperty = + DependencyProperty.RegisterAttached("AllowOnlyNumericInput", typeof(bool), typeof(TextBoxHelper), new PropertyMetadata(false, OnAllowOnlyNumericInputChanged)); + + private static void OnAllowOnlyNumericInputChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is TextBox textBox) + { + if ((bool)e.NewValue) + { + DataObject.AddPastingHandler(textBox, TextBox_Pasting); + textBox.PreviewTextInput += TextBox_PreviewTextInput; + InputMethod.SetIsInputMethodEnabled(textBox, false); + } + else + { + DataObject.RemovePastingHandler(textBox, TextBox_Pasting); + textBox.PreviewTextInput -= TextBox_PreviewTextInput; + InputMethod.SetIsInputMethodEnabled(textBox, true); + } + } + } + + + public static bool GetSelectAllOnClick(TextBox textBox) + { + return (bool)textBox.GetValue(SelectAllOnClickProperty); + } + + public static void SetSelectAllOnClick(TextBox textBox, bool value) + { + textBox.SetValue(SelectAllOnClickProperty, value); + } + + public static bool GetAllowOnlyNumericInput(DependencyObject obj) + { + return (bool)obj.GetValue(AllowOnlyNumericInputProperty); + } + + public static void SetAllowOnlyNumericInput(DependencyObject obj, bool value) + { + obj.SetValue(AllowOnlyNumericInputProperty, value); + } + + private static void TextBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (sender is TextBox textBox && !textBox.IsKeyboardFocusWithin) + { + textBox.Focus(); + textBox.SelectAll(); + e.Handled = true; + } + } + + + private static void TextBox_Pasting(object sender, DataObjectPastingEventArgs e) + { + if (e.DataObject.GetDataPresent(typeof(string))) + { + string text = (string)e.DataObject.GetData(typeof(string)); + + if (!IsNumeric(text)) + { + e.CancelCommand(); + } + } + else + { + e.CancelCommand(); + } + } + + + + private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) + { + if (!IsNumeric(e.Text) || ContainsChineseCharacters(e.Text)) + { + e.Handled = true; + } + } + + private static bool IsNumeric(string text) + { + return !string.IsNullOrEmpty(text) && text.All(char.IsDigit); + } + + private static bool ContainsChineseCharacters(string text) + { + foreach (char c in text) + { + if (c >= 0x4E00 && c <= 0x9FFF) + { + return true; + } + } + return false; + } + + } +} diff --git a/src/WPFDevelopers.Shared/Core/Utilities/ColorUtil.cs b/src/WPFDevelopers.Shared/Core/Utilities/ColorUtil.cs new file mode 100644 index 00000000..fde46ff3 --- /dev/null +++ b/src/WPFDevelopers.Shared/Core/Utilities/ColorUtil.cs @@ -0,0 +1,206 @@ +using System; +using System.Windows.Media; + +namespace WPFDevelopers.Utilities +{ + public static class ColorUtil + { + public static Color ConvertHSLToColor(Color color, double hValue = double.NaN, double sValue = double.NaN, double lValue = double.NaN) + { + double hue = hValue; + if (double.IsNaN(hue)) + hue = ColorFromH(color) % 360; + double saturation = sValue; + if (double.IsNaN(saturation)) + { + var s = ColorFromS(color); + saturation = (double) s / 100 ; + } + double lightness = lValue; + if (double.IsNaN(lightness)) + { + var l = ColorFromL(color); + lightness = (double)l / 100; + } + + double chroma = (1 - Math.Abs(2 * lightness - 1)) * saturation; + double huePrime = hue / 60.0; + double x = chroma * (1 - Math.Abs(huePrime % 2 - 1)); + + double red = 0, green = 0, blue = 0; + + if (huePrime >= 0 && huePrime < 1) + { + red = chroma; + green = x; + } + else if (huePrime >= 1 && huePrime < 2) + { + red = x; + green = chroma; + } + else if (huePrime >= 2 && huePrime < 3) + { + green = chroma; + blue = x; + } + else if (huePrime >= 3 && huePrime < 4) + { + green = x; + blue = chroma; + } + else if (huePrime >= 4 && huePrime < 5) + { + red = x; + blue = chroma; + } + else if (huePrime >= 5 && huePrime < 6) + { + red = chroma; + blue = x; + } + + double m = lightness - chroma / 2; + byte r = (byte)((red + m) * 255); + byte g = (byte)((green + m) * 255); + byte b = (byte)((blue + m) * 255); + + + return Color.FromRgb(r, g, b); + } + + public static void HsbFromColor(Color C, ref double H, ref double S, ref double B) + { + double r = C.R / 255d; + double g = C.G / 255d; + double 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); + int 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)); + } + + public static int ColorFromH(Color color) + { + double r = color.R / 255.0; + double g = color.G / 255.0; + double b = color.B / 255.0; + + double max = Math.Max(r, Math.Max(g, b)); + double min = Math.Min(r, Math.Min(g, b)); + + double hue = 0; + + if (max == min) + { + hue = 0; + } + else if (max == r) + { + hue = ((g - b) / (max - min)) % 6; + } + else if (max == g) + { + hue = ((b - r) / (max - min)) + 2; + } + else if (max == b) + { + hue = ((r - g) / (max - min)) + 4; + } + + hue *= 60; + + if (hue < 0) + { + hue += 360; + } + return (int)hue; + } + + public static int ColorFromS(Color color) + { + double r = color.R / 255.0; + double g = color.G / 255.0; + double b = color.B / 255.0; + double max = Math.Max(r, Math.Max(g, b)); + double min = Math.Min(r, Math.Min(g, b)); + double saturation = max == 0 ? 0 : (max - min) / max; + return (int)(saturation * 100); + } + + public static int ColorFromL(Color color) + { + double r = color.R / 255.0; + double g = color.G / 255.0; + double b = color.B / 255.0; + + double max = Math.Max(r, Math.Max(g, b)); + double min = Math.Min(r, Math.Min(g, b)); + + double lightness = (max + min) / 2; + + return (int)(lightness * 100); + } + + } +} diff --git a/src/WPFDevelopers.Shared/Core/Utilities/DoubleUtil.cs b/src/WPFDevelopers.Shared/Core/Utilities/DoubleUtil.cs index f7c89f25..baad7e9d 100644 --- a/src/WPFDevelopers.Shared/Core/Utilities/DoubleUtil.cs +++ b/src/WPFDevelopers.Shared/Core/Utilities/DoubleUtil.cs @@ -26,6 +26,7 @@ public static bool AreClose(double value1, double value2) public static bool LessThanOrClose(double value1, double value2) => (value1 < value2) || AreClose(value1, value2); public static bool GreaterThanOrClose(double value1, double value2) => (value1 > value2) || AreClose(value1, value2); + [StructLayout(LayoutKind.Explicit)] private struct NanUnion { @@ -34,6 +35,7 @@ private struct NanUnion [FieldOffset(0)] internal UInt64 UintValue; } + public static bool IsNaN(double value) { NanUnion t = new NanUnion(); @@ -44,5 +46,10 @@ public static bool IsNaN(double value) return (exp == 0x7ff0000000000000 || exp == 0xfff0000000000000) && (man != 0); } + + public static bool IsOne(double value) + { + return Math.Abs(value - 1.0) < 10.0 * DBL_EPSILON; + } } } diff --git a/src/WPFDevelopers.Shared/Themes/ColorPicker.xaml b/src/WPFDevelopers.Shared/Themes/ColorPicker.xaml new file mode 100644 index 00000000..59826652 --- /dev/null +++ b/src/WPFDevelopers.Shared/Themes/ColorPicker.xaml @@ -0,0 +1,325 @@ + + + + + M 15.7426,8.82842L 15.0355,9.53553L 11.5,5.99999L 7.96446,9.53553L 7.25735,8.82842L 11.5,4.58578L 15.7426,8.82842 Z M 15.7426,16.1716L 11.5,20.4142L 7.25735,16.1716L 7.96446,15.4645L 11.5,19L 15.0355,15.4645L 15.7426,16.1716 Z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +