public static Rgb AverageColor(BitmapSource source) { if (source.Format != PixelFormats.Bgra32) { throw new Exception("Unexpected Bitmap format."); } var width = source.PixelWidth; var height = source.PixelHeight; var numPixels = width * height; var bytesPerPixel = source.Format.BitsPerPixel / 8; var pixelBuffer = new byte[numPixels * bytesPerPixel]; source.CopyPixels(pixelBuffer, width * bytesPerPixel, 0); long blue = 0; long green = 0; long red = 0; for (var i = 0; i < pixelBuffer.Length; i += bytesPerPixel) { blue += pixelBuffer[i]; green += pixelBuffer[i + 1]; red += pixelBuffer[i + 2]; } return(Rgb.FromScaledValues((byte)(red / numPixels), (byte)(green / numPixels), (byte)(blue / numPixels))); }
public static Rgb PixelToRgb(BitmapSource source, int x, int y) { if (source.Format != PixelFormats.Bgra32) { throw new Exception("Unexpected Bitmap format."); } var bytesPerPixel = source.Format.BitsPerPixel / 8; var bytes = new byte[bytesPerPixel]; var rect = new Int32Rect(x, y, 1, 1); source.CopyPixels(rect, bytes, bytesPerPixel, 0); return(Rgb.FromScaledValues(bytes[2], bytes[1], bytes[0])); }
public void NewRgb_WithOutOfRangeValue_ShouldThrowException() { Assert.ThrowsException <ArgumentException>(() => new Rgb(-0.1, 0, 0)); Assert.ThrowsException <ArgumentException>(() => new Rgb(1.1, 0, 0)); Assert.ThrowsException <ArgumentException>(() => Rgb.FromScaledValues(-1, 0, 0)); Assert.ThrowsException <ArgumentException>(() => Rgb.FromScaledValues(256, 0, 0)); Assert.ThrowsException <ArgumentException>(() => new Rgb(0, -0.1, 0)); Assert.ThrowsException <ArgumentException>(() => new Rgb(0, 1.1, 0)); Assert.ThrowsException <ArgumentException>(() => Rgb.FromScaledValues(0, -1, 0)); Assert.ThrowsException <ArgumentException>(() => Rgb.FromScaledValues(0, 256, 0)); Assert.ThrowsException <ArgumentException>(() => new Rgb(0, 0, -0.1)); Assert.ThrowsException <ArgumentException>(() => new Rgb(0, 0, 1.1)); Assert.ThrowsException <ArgumentException>(() => Rgb.FromScaledValues(0, 0, -1)); Assert.ThrowsException <ArgumentException>(() => Rgb.FromScaledValues(0, 0, 256)); }
// Conversion tool for reference: // https://www.easyrgb.com/en/convert.php#inputFORM public IEnumerable <object[]> GetData(MethodInfo methodInfo) { yield return(new object[] { new ColorData { Name = "Black", Rgb = new Rgb(0, 0, 0), Hsl = new Hsl(0, 0, 0), Hsv = new Hsv(0, 0, 0), Cmyk = new Cmyk(0, 0, 0, 1), Xyz = new Xyz(0, 0, 0), Lab = new Lab(0, 0, 0) } }); yield return(new object[] { new ColorData { Name = "White", Rgb = new Rgb(1, 1, 1), Hsl = new Hsl(0, 0, 1), Hsv = new Hsv(0, 0, 1), Cmyk = new Cmyk(0, 0, 0, 0), Xyz = new Xyz(95.047, 100, 108.883), Lab = new Lab(100, 0, 0) } }); yield return(new object[] { new ColorData { Name = "Red", Rgb = new Rgb(1, 0, 0), Hsl = new Hsl(0, 1, 0.5), Hsv = new Hsv(0, 1, 1), Cmyk = new Cmyk(0, 1, 1, 0), Xyz = new Xyz(41.2456, 21.2673, 1.9334), Lab = new Lab(53.2408, 80.0925, 67.2032) } }); yield return(new object[] { new ColorData { Name = "Green", Rgb = new Rgb(0, 1, 0), Hsl = Hsl.FromScaledValues(120, 100, 50), Hsv = Hsv.FromScaledValues(120, 100, 100), Cmyk = new Cmyk(1, 0, 1, 0), Xyz = new Xyz(35.7576, 71.5152, 11.9192), Lab = new Lab(87.7347, -86.1827, 83.1793) } }); yield return(new object[] { new ColorData { Name = "Blue", Rgb = new Rgb(0, 0, 1), Hsl = Hsl.FromScaledValues(240, 100, 50), Hsv = Hsv.FromScaledValues(240, 100, 100), Cmyk = new Cmyk(1, 1, 0, 0), Xyz = new Xyz(18.0437, 7.2175, 95.0304), Lab = new Lab(32.297, 79.1875, -107.8602) } }); yield return(new object[] { new ColorData { Name = "Material Blue", Rgb = Rgb.FromScaledValues(33, 150, 243), Hsl = Hsl.FromScaledValues(206.5716, 89.744, 54.118), Hsv = Hsv.FromScaledValues(206.5716, 86.42, 95.294), Cmyk = Cmyk.FromScaledValues(86.419753, 38.271604, 0, 4.705882), Xyz = new Xyz(27.704960365063513, 28.60350077011483, 88.83745176406208), Lab = new Lab(60.4301, 2.0799, -55.1094) } }); yield return(new object[] { new ColorData { Name = "Purple HEX to HSL/HSV issue", Rgb = Rgb.FromScaledValues(116, 58, 111), Hsl = Hsl.FromScaledValues(305.172, 33.333, 34.118), Hsv = Hsv.FromScaledValues(305.172, 50, 45.49), Cmyk = Cmyk.FromScaledValues(0, 50, 4.310, 54.510), Xyz = new Xyz(11.5846, 7.8875, 15.9481), Lab = new Lab(33.7475, 33.4761, -19.6542) } }); }
public MainWindow() { InitializeComponent(); Style = (Style)FindResource(typeof(System.Windows.Window)); _vm = new MainWindowViewModel(); _vm.LoadFromPersistedData(PersistedState.Data, ColorModelTabs); DataContext = _vm; if (_vm.Rgb == Rgb.Empty) { // Material Blue – #2196F3 _vm.RefreshFromRgb(Rgb.FromScaledValues(33, 150, 243)); } // Eyedropper EyedropperButton.Click += (o, ev) => { StartEyedropper(); }; // Lighter / Darker Buttons LighterButton.Click += (o, ev) => { _vm.RefreshFromHsl(_vm.Hsl.Lighter(5)); }; DarkerButton.Click += (o, ev) => { _vm.RefreshFromHsl(_vm.Hsl.Darker(5)); }; // Color model tabs _vm.PropertyChanged += (o, ev) => { if (ev.PropertyName == "ActiveColorModelTabItem") { _vm.SaveToPersistedData(PersistedState.Data); } }; // Helper function to reduce clipboard button boilerplate: void SetupClipboardButton(Button button, Func <string> valueGetter) { EventUtil.HandleClick(button, (sender, e) => { // SetDataObject seems to work better than SetText or other methods, when programs like RealVNC are locking up the clipboard. Clipboard.SetDataObject(valueGetter()); DisplayClipboardCheckmark((Button)sender); }); } // // RGB fields // EventUtil.HandleKey(Key.Up, RgbRed, () => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(_vm.Rgb.Red + 0.01)); }); EventUtil.HandleKey(Key.Up, RgbGreen, () => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(_vm.Rgb.Green + 0.01)); }); EventUtil.HandleKey(Key.Up, RgbBlue, () => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(_vm.Rgb.Blue + 0.01)); }); EventUtil.HandleKey(Key.Up, RgbScaledRed, () => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(_vm.Rgb.ScaledRed + 1)); }); EventUtil.HandleKey(Key.Up, RgbScaledGreen, () => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(_vm.Rgb.ScaledGreen + 1)); }); EventUtil.HandleKey(Key.Up, RgbScaledBlue, () => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(_vm.Rgb.ScaledBlue + 1)); }); EventUtil.HandleKey(Key.Down, RgbRed, () => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(_vm.Rgb.Red - 0.01)); }); EventUtil.HandleKey(Key.Down, RgbGreen, () => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(_vm.Rgb.Green - 0.01)); }); EventUtil.HandleKey(Key.Down, RgbBlue, () => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(_vm.Rgb.Blue - 0.01)); }); EventUtil.HandleKey(Key.Down, RgbScaledRed, () => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(_vm.Rgb.ScaledRed - 1)); }); EventUtil.HandleKey(Key.Down, RgbScaledGreen, () => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(_vm.Rgb.ScaledGreen - 1)); }); EventUtil.HandleKey(Key.Down, RgbScaledBlue, () => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(_vm.Rgb.ScaledBlue - 1)); }); EventUtil.HandleMouseWheel( RgbRed, () => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(_vm.Rgb.Red + 0.01)); }, () => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(_vm.Rgb.Red - 0.01)); } ); EventUtil.HandleMouseWheel( RgbGreen, () => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(_vm.Rgb.Green + 0.01)); }, () => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(_vm.Rgb.Green - 0.01)); } ); EventUtil.HandleMouseWheel( RgbBlue, () => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(_vm.Rgb.Blue + 0.01)); }, () => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(_vm.Rgb.Blue - 0.01)); } ); EventUtil.HandleMouseWheel( RgbScaledRed, () => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(_vm.Rgb.ScaledRed + 1)); }, () => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(_vm.Rgb.ScaledRed - 1)); } ); EventUtil.HandleMouseWheel( RgbScaledGreen, () => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(_vm.Rgb.ScaledGreen + 1)); }, () => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(_vm.Rgb.ScaledGreen - 1)); } ); EventUtil.HandleMouseWheel( RgbScaledBlue, () => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(_vm.Rgb.ScaledBlue + 1)); }, () => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(_vm.Rgb.ScaledBlue - 1)); } ); EventUtil.HandleSliderChange(RgbRedSlider, value => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(value)); }); EventUtil.HandleSliderChange(RgbGreenSlider, value => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(value)); }); EventUtil.HandleSliderChange(RgbBlueSlider, value => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(value)); }); _vm.PropertyChangedByUser += (o, ev) => { int intVal; bool isInt; bool isDouble; double doubleVal; Rgb? nullableRgb; switch (ev.PropertyName) { case nameof(MainWindowViewModel.RgbText): nullableRgb = Rgb.FromString(_vm.RgbText); if (nullableRgb.HasValue) { _vm.RefreshFromRgb(nullableRgb.Value); } break; case nameof(MainWindowViewModel.RgbScaledText): nullableRgb = Rgb.FromScaledString(_vm.RgbScaledText); if (nullableRgb.HasValue) { _vm.RefreshFromRgb(nullableRgb.Value); } break; case nameof(MainWindowViewModel.RgbScaledRed): isInt = int.TryParse(_vm.RgbScaledRed, out intVal); if (!isInt || !Rgb.IsValidScaledComponent(intVal)) { return; } _vm.RefreshFromRgb(_vm.Rgb.WithRed(intVal)); break; case nameof(MainWindowViewModel.RgbScaledGreen): isInt = int.TryParse(_vm.RgbScaledGreen, out intVal); if (!isInt || !Rgb.IsValidScaledComponent(intVal)) { return; } _vm.RefreshFromRgb(_vm.Rgb.WithGreen(intVal)); break; case nameof(MainWindowViewModel.RgbScaledBlue): isInt = int.TryParse(_vm.RgbScaledBlue, out intVal); if (!isInt || !Rgb.IsValidScaledComponent(intVal)) { return; } _vm.RefreshFromRgb(_vm.Rgb.WithBlue(intVal)); break; case nameof(MainWindowViewModel.RgbRed): isDouble = double.TryParse(_vm.RgbRed, out doubleVal); if (!isDouble || !Rgb.IsValidComponent(doubleVal)) { return; } _vm.RefreshFromRgb(_vm.Rgb.WithRed(doubleVal)); break; case nameof(MainWindowViewModel.RgbGreen): isDouble = double.TryParse(_vm.RgbGreen, out doubleVal); if (!isDouble || !Rgb.IsValidComponent(doubleVal)) { return; } _vm.RefreshFromRgb(_vm.Rgb.WithGreen(doubleVal)); break; case nameof(MainWindowViewModel.RgbBlue): isDouble = double.TryParse(_vm.RgbBlue, out doubleVal); if (!isDouble || !Rgb.IsValidComponent(doubleVal)) { return; } _vm.RefreshFromRgb(_vm.Rgb.WithBlue(doubleVal)); break; } }; SetupClipboardButton(RgbScaledTextClipboardButton, () => _vm.RgbScaledText); SetupClipboardButton(RgbTextClipboardButton, () => _vm.RgbText); // // HEX fields // EventUtil.HandleKey(Key.Up, HexRed, () => { _vm.RefreshFromHex(_vm.Hex.WithRed(_vm.Hex.Red + 1)); }); EventUtil.HandleKey(Key.Up, HexGreen, () => { _vm.RefreshFromHex(_vm.Hex.WithGreen(_vm.Hex.Green + 1)); }); EventUtil.HandleKey(Key.Up, HexBlue, () => { _vm.RefreshFromHex(_vm.Hex.WithBlue(_vm.Hex.Blue + 1)); }); EventUtil.HandleKey(Key.Down, HexRed, () => { _vm.RefreshFromHex(_vm.Hex.WithRed(_vm.Hex.Red - 1)); }); EventUtil.HandleKey(Key.Down, HexGreen, () => { _vm.RefreshFromHex(_vm.Hex.WithGreen(_vm.Hex.Green - 1)); }); EventUtil.HandleKey(Key.Down, HexBlue, () => { _vm.RefreshFromHex(_vm.Hex.WithBlue(_vm.Hex.Blue - 1)); }); EventUtil.HandleMouseWheel( HexRed, () => { _vm.RefreshFromHex(_vm.Hex.WithRed(_vm.Hex.Red + 1)); }, () => { _vm.RefreshFromHex(_vm.Hex.WithRed(_vm.Hex.Red - 1)); } ); EventUtil.HandleMouseWheel( HexGreen, () => { _vm.RefreshFromHex(_vm.Hex.WithGreen(_vm.Hex.Green + 1)); }, () => { _vm.RefreshFromHex(_vm.Hex.WithGreen(_vm.Hex.Green - 1)); } ); EventUtil.HandleMouseWheel( HexBlue, () => { _vm.RefreshFromHex(_vm.Hex.WithBlue(_vm.Hex.Blue + 1)); }, () => { _vm.RefreshFromHex(_vm.Hex.WithBlue(_vm.Hex.Blue - 1)); } ); EventUtil.HandleSliderChange(HexRedSlider, value => { _vm.RefreshFromRgb(_vm.Rgb.WithRed(value)); }); EventUtil.HandleSliderChange(HexGreenSlider, value => { _vm.RefreshFromRgb(_vm.Rgb.WithGreen(value)); }); EventUtil.HandleSliderChange(HexBlueSlider, value => { _vm.RefreshFromRgb(_vm.Rgb.WithBlue(value)); }); // The box actively ignores 3-char hex strings while typing so that 6-chars may be entered without interruption. // However, 3-char hex strings can still be entered by pressing Enter or dropping focus: EventUtil.HandleInputEnterOrFocusLost(HexText, value => { var nullableHex = Hex.FromString(_vm.HexText); if (nullableHex.HasValue) { _vm.RefreshFromHex(nullableHex.Value); } }); _vm.PropertyChangedByUser += (o, ev) => { switch (ev.PropertyName) { case nameof(MainWindowViewModel.HexText): var nullableHex = Hex.From6CharString(_vm.HexText); if (nullableHex.HasValue) { _vm.RefreshFromHex(nullableHex.Value); } break; case nameof(MainWindowViewModel.HexRed): // Length comparison for better UX. Stops converting "0" to "00" and moving user cursor. if (!Hex.IsValidHexPart(_vm.HexRed) || _vm.HexRed.Length != 2) { return; } _vm.RefreshFromHex(_vm.Hex.WithRed(_vm.HexRed)); break; case nameof(MainWindowViewModel.HexGreen): // Length comparison for better UX. Stops converting "0" to "00" and moving user cursor. if (!Hex.IsValidHexPart(_vm.HexGreen) || _vm.HexGreen.Length != 2) { return; } _vm.RefreshFromHex(_vm.Hex.WithGreen(_vm.HexGreen)); break; case nameof(MainWindowViewModel.HexBlue): // Length comparison for better UX. Stops converting "0" to "00" and moving user cursor. if (!Hex.IsValidHexPart(_vm.HexBlue) || _vm.HexBlue.Length != 2) { return; } _vm.RefreshFromHex(_vm.Hex.WithBlue(_vm.HexBlue)); break; } }; SetupClipboardButton(HexTextClipboardButton, () => _vm.HexText); // // HSL fields // EventUtil.HandleKey(Key.Up, HslHue, () => { _vm.RefreshFromHsl(_vm.Hsl.WithHue(_vm.Hsl.Hue + 0.01)); }); EventUtil.HandleKey(Key.Up, HslSaturation, () => { _vm.RefreshFromHsl(_vm.Hsl.WithSaturation(_vm.Hsl.Saturation + 0.01)); }); EventUtil.HandleKey(Key.Up, HslLuminance, () => { _vm.RefreshFromHsl(_vm.Hsl.WithLuminance(_vm.Hsl.Luminance + 0.01)); }); EventUtil.HandleKey(Key.Up, HslScaledHue, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledHue(_vm.Hsl.ScaledHue + 1)); }); EventUtil.HandleKey(Key.Up, HslScaledSaturation, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledSaturation(_vm.Hsl.ScaledSaturation + 1)); }); EventUtil.HandleKey(Key.Up, HslScaledLuminance, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledLuminance(_vm.Hsl.ScaledLuminance + 1)); }); EventUtil.HandleKey(Key.Down, HslHue, () => { _vm.RefreshFromHsl(_vm.Hsl.WithHue(_vm.Hsl.Hue - 0.01)); }); EventUtil.HandleKey(Key.Down, HslSaturation, () => { _vm.RefreshFromHsl(_vm.Hsl.WithSaturation(_vm.Hsl.Saturation - 0.01)); }); EventUtil.HandleKey(Key.Down, HslLuminance, () => { _vm.RefreshFromHsl(_vm.Hsl.WithLuminance(_vm.Hsl.Luminance - 0.01)); }); EventUtil.HandleKey(Key.Down, HslScaledHue, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledHue(_vm.Hsl.ScaledHue - 1)); }); EventUtil.HandleKey(Key.Down, HslScaledSaturation, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledSaturation(_vm.Hsl.ScaledSaturation - 1)); }); EventUtil.HandleKey(Key.Down, HslScaledLuminance, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledLuminance(_vm.Hsl.ScaledLuminance - 1)); }); EventUtil.HandleMouseWheel( HslHue, () => { _vm.RefreshFromHsl(_vm.Hsl.WithHue(_vm.Hsl.Hue + 0.01)); }, () => { _vm.RefreshFromHsl(_vm.Hsl.WithHue(_vm.Hsl.Hue - 0.01)); } ); EventUtil.HandleMouseWheel( HslSaturation, () => { _vm.RefreshFromHsl(_vm.Hsl.WithSaturation(_vm.Hsl.Saturation + 0.01)); }, () => { _vm.RefreshFromHsl(_vm.Hsl.WithSaturation(_vm.Hsl.Saturation - 0.01)); } ); EventUtil.HandleMouseWheel( HslLuminance, () => { _vm.RefreshFromHsl(_vm.Hsl.WithLuminance(_vm.Hsl.Luminance + 0.01)); }, () => { _vm.RefreshFromHsl(_vm.Hsl.WithLuminance(_vm.Hsl.Luminance - 0.01)); } ); EventUtil.HandleMouseWheel( HslScaledHue, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledHue(_vm.Hsl.ScaledHue + 1)); }, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledHue(_vm.Hsl.ScaledHue - 1)); } ); EventUtil.HandleMouseWheel( HslScaledSaturation, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledSaturation(_vm.Hsl.ScaledSaturation + 1)); }, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledSaturation(_vm.Hsl.ScaledSaturation - 1)); } ); EventUtil.HandleMouseWheel( HslScaledLuminance, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledLuminance(_vm.Hsl.ScaledLuminance + 1)); }, () => { _vm.RefreshFromHsl(_vm.Hsl.WithScaledLuminance(_vm.Hsl.ScaledLuminance - 1)); } ); EventUtil.HandleSliderChange(HslHueSlider, value => { _vm.RefreshFromHsl(_vm.Hsl.WithHue(value)); }); EventUtil.HandleSliderChange(HslSaturationSlider, value => { _vm.RefreshFromHsl(_vm.Hsl.WithSaturation(value)); }); EventUtil.HandleSliderChange(HslLuminanceSlider, value => { _vm.RefreshFromHsl(_vm.Hsl.WithLuminance(value)); }); _vm.PropertyChangedByUser += (o, ev) => { bool isDouble; double doubleVal; Hsl? nullableHsl; switch (ev.PropertyName) { case nameof(MainWindowViewModel.HslText): nullableHsl = Hsl.FromString(_vm.HslText); if (nullableHsl.HasValue) { _vm.RefreshFromHsl(nullableHsl.Value); } break; case nameof(MainWindowViewModel.HslScaledText): nullableHsl = Hsl.FromScaledString(_vm.HslScaledText); if (nullableHsl.HasValue) { _vm.RefreshFromHsl(nullableHsl.Value); } break; case nameof(MainWindowViewModel.HslHue): isDouble = double.TryParse(_vm.HslHue, out doubleVal); if (!isDouble || !Hsl.IsValidHue(doubleVal)) { return; } _vm.RefreshFromHsl(_vm.Hsl.WithHue(doubleVal)); break; case nameof(MainWindowViewModel.HslSaturation): isDouble = double.TryParse(_vm.HslSaturation, out doubleVal); if (!isDouble || !Hsl.IsValidSaturation(doubleVal)) { return; } _vm.RefreshFromHsl(_vm.Hsl.WithSaturation(doubleVal)); break; case nameof(MainWindowViewModel.HslLuminance): isDouble = double.TryParse(_vm.HslLuminance, out doubleVal); if (!isDouble || !Hsl.IsValidLuminance(doubleVal)) { return; } _vm.RefreshFromHsl(_vm.Hsl.WithLuminance(doubleVal)); break; case nameof(MainWindowViewModel.HslScaledHue): isDouble = double.TryParse(_vm.HslScaledHue, out doubleVal); if (!isDouble || !Hsl.IsValidScaledHue(doubleVal)) { return; } _vm.RefreshFromHsl(_vm.Hsl.WithScaledHue(doubleVal)); break; case nameof(MainWindowViewModel.HslScaledSaturation): isDouble = double.TryParse(_vm.HslScaledHue, out doubleVal); if (!isDouble || !Hsl.IsValidScaledSaturation(doubleVal)) { return; } _vm.RefreshFromHsl(_vm.Hsl.WithScaledSaturation(doubleVal)); break; case nameof(MainWindowViewModel.HslScaledLuminance): isDouble = double.TryParse(_vm.HslScaledHue, out doubleVal); if (!isDouble || !Hsl.IsValidScaledLuminance(doubleVal)) { return; } _vm.RefreshFromHsl(_vm.Hsl.WithScaledLuminance(doubleVal)); break; } }; SetupClipboardButton(HslScaledTextClipboardButton, () => _vm.HslScaledText); SetupClipboardButton(HslTextClipboardButton, () => _vm.HslText); // // HSV fields // EventUtil.HandleKey(Key.Up, HsvHue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithHue(_vm.Hsv.Hue + 0.01)); }); EventUtil.HandleKey(Key.Up, HsvSaturation, () => { _vm.RefreshFromHsv(_vm.Hsv.WithSaturation(_vm.Hsv.Saturation + 0.01)); }); EventUtil.HandleKey(Key.Up, HsvValue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithValue(_vm.Hsv.Value + 0.01)); }); EventUtil.HandleKey(Key.Up, HsvScaledHue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledHue(_vm.Hsv.ScaledHue + 1)); }); EventUtil.HandleKey(Key.Up, HsvScaledSaturation, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledSaturation(_vm.Hsv.ScaledSaturation + 1)); }); EventUtil.HandleKey(Key.Up, HsvScaledValue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledValue(_vm.Hsv.ScaledValue + 1)); }); EventUtil.HandleKey(Key.Down, HsvHue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithHue(_vm.Hsv.Hue - 0.01)); }); EventUtil.HandleKey(Key.Down, HsvSaturation, () => { _vm.RefreshFromHsv(_vm.Hsv.WithSaturation(_vm.Hsv.Saturation - 0.01)); }); EventUtil.HandleKey(Key.Down, HsvValue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithValue(_vm.Hsv.Value - 0.01)); }); EventUtil.HandleKey(Key.Down, HsvScaledHue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledHue(_vm.Hsv.ScaledHue - 1)); }); EventUtil.HandleKey(Key.Down, HsvScaledSaturation, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledSaturation(_vm.Hsv.ScaledSaturation - 1)); }); EventUtil.HandleKey(Key.Down, HsvScaledValue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledValue(_vm.Hsv.ScaledValue - 1)); }); EventUtil.HandleMouseWheel(HsvHue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithHue(_vm.Hsv.Hue + 0.01)); }, () => { _vm.RefreshFromHsv(_vm.Hsv.WithHue(_vm.Hsv.Hue - 0.01)); }); EventUtil.HandleMouseWheel(HsvSaturation, () => { _vm.RefreshFromHsv(_vm.Hsv.WithSaturation(_vm.Hsv.Saturation + 0.01)); }, () => { _vm.RefreshFromHsv(_vm.Hsv.WithSaturation(_vm.Hsv.Saturation - 0.01)); }); EventUtil.HandleMouseWheel(HsvValue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithValue(_vm.Hsv.Value + 0.01)); }, () => { _vm.RefreshFromHsv(_vm.Hsv.WithValue(_vm.Hsv.Value - 0.01)); }); EventUtil.HandleMouseWheel(HsvScaledHue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledHue(_vm.Hsv.ScaledHue + 1)); }, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledHue(_vm.Hsv.ScaledHue - 1)); }); EventUtil.HandleMouseWheel(HsvScaledSaturation, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledSaturation(_vm.Hsv.ScaledSaturation + 1)); }, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledSaturation(_vm.Hsv.ScaledSaturation - 1)); }); EventUtil.HandleMouseWheel(HsvScaledValue, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledValue(_vm.Hsv.ScaledValue + 1)); }, () => { _vm.RefreshFromHsv(_vm.Hsv.WithScaledValue(_vm.Hsv.ScaledValue - 1)); }); EventUtil.HandleSliderChange(HsvHueSlider, value => { _vm.RefreshFromHsv(_vm.Hsv.WithHue(value)); }); EventUtil.HandleSliderChange(HsvSaturationSlider, value => { _vm.RefreshFromHsv(_vm.Hsv.WithSaturation(value)); }); EventUtil.HandleSliderChange(HsvValueSlider, value => { _vm.RefreshFromHsv(_vm.Hsv.WithValue(value)); }); _vm.PropertyChangedByUser += (o, ev) => { bool isDouble; double doubleVal; Hsv? nullableHsv; switch (ev.PropertyName) { case nameof(MainWindowViewModel.HsvText): nullableHsv = Hsv.FromString(_vm.HsvText); if (nullableHsv.HasValue) { _vm.RefreshFromHsv(nullableHsv.Value); } break; case nameof(MainWindowViewModel.HsvScaledText): nullableHsv = Hsv.FromScaledString(_vm.HsvScaledText); if (nullableHsv.HasValue) { _vm.RefreshFromHsv(nullableHsv.Value); } break; case nameof(MainWindowViewModel.HsvHue): isDouble = double.TryParse(_vm.HsvHue, out doubleVal); if (!isDouble || !Hsv.IsValidHue(doubleVal)) { return; } _vm.RefreshFromHsv(_vm.Hsv.WithHue(doubleVal)); break; case nameof(MainWindowViewModel.HsvSaturation): isDouble = double.TryParse(_vm.HsvSaturation, out doubleVal); if (!isDouble || !Hsv.IsValidSaturation(doubleVal)) { return; } _vm.RefreshFromHsv(_vm.Hsv.WithSaturation(doubleVal)); break; case nameof(MainWindowViewModel.HsvValue): isDouble = double.TryParse(_vm.HsvValue, out doubleVal); if (!isDouble || !Hsv.IsValidValue(doubleVal)) { return; } _vm.RefreshFromHsv(_vm.Hsv.WithValue(doubleVal)); break; case nameof(MainWindowViewModel.HsvScaledHue): isDouble = double.TryParse(_vm.HsvScaledHue, out doubleVal); if (!isDouble || !Hsv.IsValidScaledHue(doubleVal)) { return; } _vm.RefreshFromHsv(_vm.Hsv.WithScaledHue(doubleVal)); break; case nameof(MainWindowViewModel.HsvScaledSaturation): isDouble = double.TryParse(_vm.HsvScaledHue, out doubleVal); if (!isDouble || !Hsv.IsValidScaledSaturation(doubleVal)) { return; } _vm.RefreshFromHsv(_vm.Hsv.WithScaledSaturation(doubleVal)); break; case nameof(MainWindowViewModel.HsvScaledValue): isDouble = double.TryParse(_vm.HsvScaledHue, out doubleVal); if (!isDouble || !Hsv.IsValidScaledValue(doubleVal)) { return; } _vm.RefreshFromHsv(_vm.Hsv.WithScaledValue(doubleVal)); break; } }; SetupClipboardButton(HsvScaledTextClipboardButton, () => _vm.HsvScaledText); SetupClipboardButton(HsvTextClipboardButton, () => _vm.HsvText); // // CMYK fields // EventUtil.HandleKey(Key.Up, CmykCyan, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithCyan(_vm.Cmyk.Cyan + 0.01)); }); EventUtil.HandleKey(Key.Up, CmykMagenta, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithMagenta(_vm.Cmyk.Magenta + 0.01)); }); EventUtil.HandleKey(Key.Up, CmykYellow, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithYellow(_vm.Cmyk.Yellow + 0.01)); }); EventUtil.HandleKey(Key.Up, CmykKey, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithKey(_vm.Cmyk.Key + 0.01)); }); EventUtil.HandleKey(Key.Up, CmykScaledCyan, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledCyan(_vm.Cmyk.ScaledCyan + 1)); }); EventUtil.HandleKey(Key.Up, CmykScaledMagenta, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledMagenta(_vm.Cmyk.ScaledMagenta + 1)); }); EventUtil.HandleKey(Key.Up, CmykScaledYellow, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledYellow(_vm.Cmyk.ScaledYellow + 1)); }); EventUtil.HandleKey(Key.Up, CmykScaledKey, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledKey(_vm.Cmyk.ScaledKey + 1)); }); EventUtil.HandleKey(Key.Down, CmykCyan, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithCyan(_vm.Cmyk.Cyan - 0.01)); }); EventUtil.HandleKey(Key.Down, CmykMagenta, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithMagenta(_vm.Cmyk.Magenta - 0.01)); }); EventUtil.HandleKey(Key.Down, CmykYellow, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithYellow(_vm.Cmyk.Yellow - 0.01)); }); EventUtil.HandleKey(Key.Down, CmykKey, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithKey(_vm.Cmyk.Key - 0.01)); }); EventUtil.HandleKey(Key.Down, CmykScaledCyan, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledCyan(_vm.Cmyk.ScaledCyan - 1)); }); EventUtil.HandleKey(Key.Down, CmykScaledMagenta, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledMagenta(_vm.Cmyk.ScaledMagenta - 1)); }); EventUtil.HandleKey(Key.Down, CmykScaledYellow, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledYellow(_vm.Cmyk.ScaledYellow - 1)); }); EventUtil.HandleKey(Key.Down, CmykScaledKey, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledKey(_vm.Cmyk.ScaledKey - 1)); }); EventUtil.HandleMouseWheel(CmykCyan, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithCyan(_vm.Cmyk.Cyan + 0.01)); }, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithCyan(_vm.Cmyk.Cyan - 0.01)); }); EventUtil.HandleMouseWheel(CmykMagenta, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithMagenta(_vm.Cmyk.Magenta + 0.01)); }, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithMagenta(_vm.Cmyk.Magenta - 0.01)); }); EventUtil.HandleMouseWheel(CmykYellow, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithYellow(_vm.Cmyk.Yellow + 0.01)); }, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithYellow(_vm.Cmyk.Yellow - 0.01)); }); EventUtil.HandleMouseWheel(CmykKey, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithKey(_vm.Cmyk.Key + 0.01)); }, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithKey(_vm.Cmyk.Key - 0.01)); }); EventUtil.HandleMouseWheel(CmykScaledCyan, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledCyan(_vm.Cmyk.ScaledCyan + 1)); }, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledCyan(_vm.Cmyk.ScaledCyan - 1)); }); EventUtil.HandleMouseWheel(CmykScaledMagenta, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledMagenta(_vm.Cmyk.ScaledMagenta + 1)); }, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledMagenta(_vm.Cmyk.ScaledMagenta - 1)); }); EventUtil.HandleMouseWheel(CmykScaledYellow, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledYellow(_vm.Cmyk.ScaledYellow + 1)); }, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledYellow(_vm.Cmyk.ScaledYellow - 1)); }); EventUtil.HandleMouseWheel(CmykScaledKey, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledKey(_vm.Cmyk.ScaledKey + 1)); }, () => { _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledKey(_vm.Cmyk.ScaledKey - 1)); }); EventUtil.HandleSliderChange(CmykCyanSlider, value => { _vm.RefreshFromCmyk(_vm.Cmyk.WithCyan(value)); }); EventUtil.HandleSliderChange(CmykMagentaSlider, value => { _vm.RefreshFromCmyk(_vm.Cmyk.WithMagenta(value)); }); EventUtil.HandleSliderChange(CmykYellowSlider, value => { _vm.RefreshFromCmyk(_vm.Cmyk.WithYellow(value)); }); EventUtil.HandleSliderChange(CmykKeySlider, value => { _vm.RefreshFromCmyk(_vm.Cmyk.WithKey(value)); }); _vm.PropertyChangedByUser += (o, ev) => { bool isDouble; double doubleVal; Cmyk? nullableCmyk; switch (ev.PropertyName) { case nameof(MainWindowViewModel.CmykText): nullableCmyk = Cmyk.FromString(_vm.CmykText); if (nullableCmyk.HasValue) { _vm.RefreshFromCmyk(nullableCmyk.Value); } break; case nameof(MainWindowViewModel.CmykScaledText): nullableCmyk = Cmyk.FromScaledString(_vm.CmykScaledText); if (nullableCmyk.HasValue) { _vm.RefreshFromCmyk(nullableCmyk.Value); } break; case nameof(MainWindowViewModel.CmykCyan): isDouble = double.TryParse(_vm.CmykCyan, out doubleVal); if (!isDouble || !Cmyk.IsValidComponent(doubleVal)) { return; } _vm.RefreshFromCmyk(_vm.Cmyk.WithCyan(doubleVal)); break; case nameof(MainWindowViewModel.CmykMagenta): isDouble = double.TryParse(_vm.CmykMagenta, out doubleVal); if (!isDouble || !Cmyk.IsValidComponent(doubleVal)) { return; } _vm.RefreshFromCmyk(_vm.Cmyk.WithMagenta(doubleVal)); break; case nameof(MainWindowViewModel.CmykYellow): isDouble = double.TryParse(_vm.CmykYellow, out doubleVal); if (!isDouble || !Cmyk.IsValidComponent(doubleVal)) { return; } _vm.RefreshFromCmyk(_vm.Cmyk.WithYellow(doubleVal)); break; case nameof(MainWindowViewModel.CmykKey): isDouble = double.TryParse(_vm.CmykKey, out doubleVal); if (!isDouble || !Cmyk.IsValidComponent(doubleVal)) { return; } _vm.RefreshFromCmyk(_vm.Cmyk.WithKey(doubleVal)); break; case nameof(MainWindowViewModel.CmykScaledCyan): isDouble = double.TryParse(_vm.CmykScaledCyan, out doubleVal); if (!isDouble || !Cmyk.IsValidScaledComponent(doubleVal)) { return; } _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledCyan(doubleVal)); break; case nameof(MainWindowViewModel.CmykScaledMagenta): isDouble = double.TryParse(_vm.CmykScaledMagenta, out doubleVal); if (!isDouble || !Cmyk.IsValidScaledComponent(doubleVal)) { return; } _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledMagenta(doubleVal)); break; case nameof(MainWindowViewModel.CmykScaledYellow): isDouble = double.TryParse(_vm.CmykScaledYellow, out doubleVal); if (!isDouble || !Cmyk.IsValidScaledComponent(doubleVal)) { return; } _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledYellow(doubleVal)); break; case nameof(MainWindowViewModel.CmykScaledKey): isDouble = double.TryParse(_vm.CmykScaledKey, out doubleVal); if (!isDouble || !Cmyk.IsValidScaledComponent(doubleVal)) { return; } _vm.RefreshFromCmyk(_vm.Cmyk.WithScaledKey(doubleVal)); break; } }; SetupClipboardButton(CmykScaledTextClipboardButton, () => _vm.CmykScaledText); SetupClipboardButton(CmykTextClipboardButton, () => _vm.CmykText); // // LAB fields // EventUtil.HandleKey(Key.Up, LabL, () => { _vm.RefreshFromLab(_vm.Lab.WithL(_vm.Lab.L + 1)); }); EventUtil.HandleKey(Key.Up, LabA, () => { _vm.RefreshFromLab(_vm.Lab.WithA(_vm.Lab.A + 1)); }); EventUtil.HandleKey(Key.Up, LabB, () => { _vm.RefreshFromLab(_vm.Lab.WithB(_vm.Lab.B + 1)); }); EventUtil.HandleKey(Key.Down, LabL, () => { _vm.RefreshFromLab(_vm.Lab.WithL(_vm.Lab.L - 1)); }); EventUtil.HandleKey(Key.Down, LabA, () => { _vm.RefreshFromLab(_vm.Lab.WithA(_vm.Lab.A - 1)); }); EventUtil.HandleKey(Key.Down, LabB, () => { _vm.RefreshFromLab(_vm.Lab.WithB(_vm.Lab.B - 1)); }); EventUtil.HandleMouseWheel(LabL, () => { _vm.RefreshFromLab(_vm.Lab.WithL(_vm.Lab.L + 1)); }, () => { _vm.RefreshFromLab(_vm.Lab.WithL(_vm.Lab.L - 1)); }); EventUtil.HandleMouseWheel(LabA, () => { _vm.RefreshFromLab(_vm.Lab.WithA(_vm.Lab.A + 1)); }, () => { _vm.RefreshFromLab(_vm.Lab.WithA(_vm.Lab.A - 1)); }); EventUtil.HandleMouseWheel(LabB, () => { _vm.RefreshFromLab(_vm.Lab.WithB(_vm.Lab.B + 1)); }, () => { _vm.RefreshFromLab(_vm.Lab.WithB(_vm.Lab.B - 1)); }); EventUtil.HandleSliderChange(LabLSlider, value => { _vm.RefreshFromLab(_vm.Lab.WithL(value)); }); EventUtil.HandleSliderChange(LabASlider, value => { _vm.RefreshFromLab(_vm.Lab.WithA(value)); }); EventUtil.HandleSliderChange(LabBSlider, value => { _vm.RefreshFromLab(_vm.Lab.WithB(value)); }); _vm.PropertyChangedByUser += (o, ev) => { switch (ev.PropertyName) { case nameof(MainWindowViewModel.LabText): var nullableLab = Lab.FromString(_vm.LabText); if (nullableLab.HasValue) { _vm.RefreshFromLab(nullableLab.Value); } break; case nameof(MainWindowViewModel.LabL): if (!Lab.IsValidL(_vm.LabL)) { return; } _vm.RefreshFromLab(_vm.Lab.WithL(_vm.LabL)); break; case nameof(MainWindowViewModel.LabA): if (!Lab.IsValidA(_vm.LabA)) { return; } _vm.RefreshFromLab(_vm.Lab.WithA(_vm.LabA)); break; case nameof(MainWindowViewModel.LabB): if (!Lab.IsValidB(_vm.LabB)) { return; } _vm.RefreshFromLab(_vm.Lab.WithB(_vm.LabB)); break; } }; SetupClipboardButton(LabTextClipboardButton, () => _vm.LabText); // // ---- // // Hack to bring window to front on start. (I had instances where I would open the tool // only for it to come up behind my current window.) if (!IsVisible) { Show(); } WindowState = WindowState.Normal; Activate(); Topmost = true; Topmost = false; Focus(); }