using System; using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; namespace AIStudio.Wpf.Mind.Controls { public enum ColorMode { ColorPalette, ColorCanvas } public enum ColorSortingMode { Alphabetical, HueSaturationBrightness } [TemplatePart(Name = PART_AvailableColors, Type = typeof(ListBox))] [TemplatePart(Name = PART_StandardColors, Type = typeof(ListBox))] [TemplatePart(Name = PART_RecentColors, Type = typeof(ListBox))] [TemplatePart(Name = PART_ColorPickerToggleButton, Type = typeof(ToggleButton))] [TemplatePart(Name = PART_ColorPickerPalettePopup, Type = typeof(Popup))] public class ColorPicker : Control { private const string PART_AvailableColors = "PART_AvailableColors"; private const string PART_StandardColors = "PART_StandardColors"; private const string PART_RecentColors = "PART_RecentColors"; private const string PART_ColorPickerToggleButton = "PART_ColorPickerToggleButton"; private const string PART_ColorPickerPalettePopup = "PART_ColorPickerPalettePopup"; #region Members private ListBox _availableColors; private ListBox _standardColors; private ListBox _recentColors; private ToggleButton _toggleButton; private Popup _popup; private Color? _initialColor; private bool _selectionChanged; #endregion //Members #region Properties #region AdvancedButtonHeader public static readonly DependencyProperty AdvancedButtonHeaderProperty = DependencyProperty.Register("AdvancedButtonHeader", typeof(string), typeof(ColorPicker), new UIPropertyMetadata("Advanced")); public string AdvancedButtonHeader { get { return (string)GetValue(AdvancedButtonHeaderProperty); } set { SetValue(AdvancedButtonHeaderProperty, value); } } #endregion //AdvancedButtonHeader #region AvailableColors public static readonly DependencyProperty AvailableColorsProperty = DependencyProperty.Register("AvailableColors", typeof(ObservableCollection), typeof(ColorPicker), new UIPropertyMetadata(CreateAvailableColors())); public ObservableCollection AvailableColors { get { return (ObservableCollection)GetValue(AvailableColorsProperty); } set { SetValue(AvailableColorsProperty, value); } } #endregion //AvailableColors #region AvailableColorsSortingMode public static readonly DependencyProperty AvailableColorsSortingModeProperty = DependencyProperty.Register("AvailableColorsSortingMode", typeof(ColorSortingMode), typeof(ColorPicker), new UIPropertyMetadata(ColorSortingMode.Alphabetical, OnAvailableColorsSortingModeChanged)); public ColorSortingMode AvailableColorsSortingMode { get { return (ColorSortingMode)GetValue(AvailableColorsSortingModeProperty); } set { SetValue(AvailableColorsSortingModeProperty, value); } } private static void OnAvailableColorsSortingModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ColorPicker colorPicker = (ColorPicker)d; if (colorPicker != null) colorPicker.OnAvailableColorsSortingModeChanged((ColorSortingMode)e.OldValue, (ColorSortingMode)e.NewValue); } private void OnAvailableColorsSortingModeChanged(ColorSortingMode oldValue, ColorSortingMode newValue) { ListCollectionView lcv = (ListCollectionView)(CollectionViewSource.GetDefaultView(this.AvailableColors)); if (lcv != null) { lcv.CustomSort = (AvailableColorsSortingMode == ColorSortingMode.HueSaturationBrightness) ? new ColorSorter() : null; } } #endregion //AvailableColorsSortingMode #region AvailableColorsHeader public static readonly DependencyProperty AvailableColorsHeaderProperty = DependencyProperty.Register("AvailableColorsHeader", typeof(string), typeof(ColorPicker), new UIPropertyMetadata("Available Colors")); public string AvailableColorsHeader { get { return (string)GetValue(AvailableColorsHeaderProperty); } set { SetValue(AvailableColorsHeaderProperty, value); } } #endregion //AvailableColorsHeader #region ButtonStyle public static readonly DependencyProperty ButtonStyleProperty = DependencyProperty.Register("ButtonStyle", typeof(Style), typeof(ColorPicker)); public Style ButtonStyle { get { return (Style)GetValue(ButtonStyleProperty); } set { SetValue(ButtonStyleProperty, value); } } #endregion //ButtonStyle #region ColorMode public static readonly DependencyProperty ColorModeProperty = DependencyProperty.Register("ColorMode", typeof(ColorMode), typeof(ColorPicker), new UIPropertyMetadata(ColorMode.ColorPalette)); public ColorMode ColorMode { get { return (ColorMode)GetValue(ColorModeProperty); } set { SetValue(ColorModeProperty, value); } } #endregion //ColorMode #region IsOpen public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen", typeof(bool), typeof(ColorPicker), new UIPropertyMetadata(false, OnIsOpenChanged)); public bool IsOpen { get { return (bool)GetValue(IsOpenProperty); } set { SetValue(IsOpenProperty, value); } } private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ColorPicker colorPicker = (ColorPicker)d; if (colorPicker != null) colorPicker.OnIsOpenChanged((bool)e.OldValue, (bool)e.NewValue); if (colorPicker.IsOpen == false) { } } private void OnIsOpenChanged(bool oldValue, bool newValue) { if (newValue) { _initialColor = this.SelectedColor; } RoutedEventArgs args = new RoutedEventArgs(newValue ? OpenedEvent : ClosedEvent, this); this.RaiseEvent(args); } #endregion //IsOpen #region MaxDropDownWidth public static readonly DependencyProperty MaxDropDownWidthProperty = DependencyProperty.Register("MaxDropDownWidth", typeof(double) , typeof(ColorPicker), new UIPropertyMetadata(214d)); public double MaxDropDownWidth { get { return (double)GetValue(MaxDropDownWidthProperty); } set { SetValue(MaxDropDownWidthProperty, value); } } private static void OnMaxDropDownWidthChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { var colorPicker = o as ColorPicker; if (colorPicker != null) colorPicker.OnMaxDropDownWidthChanged((double)e.OldValue, (double)e.NewValue); } protected virtual void OnMaxDropDownWidthChanged(double oldValue, double newValue) { } #endregion #region RecentColors public static readonly DependencyProperty RecentColorsProperty = DependencyProperty.Register("RecentColors", typeof(ObservableCollection), typeof(ColorPicker), new UIPropertyMetadata(null)); public ObservableCollection RecentColors { get { return (ObservableCollection)GetValue(RecentColorsProperty); } set { SetValue(RecentColorsProperty, value); } } #endregion //RecentColors #region RecentColorsHeader public static readonly DependencyProperty RecentColorsHeaderProperty = DependencyProperty.Register("RecentColorsHeader", typeof(string), typeof(ColorPicker), new UIPropertyMetadata("Recent Colors")); public string RecentColorsHeader { get { return (string)GetValue(RecentColorsHeaderProperty); } set { SetValue(RecentColorsHeaderProperty, value); } } #endregion //RecentColorsHeader #region SelectedColor public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor", typeof(Color?), typeof(ColorPicker), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnSelectedColorPropertyChanged))); public Color? SelectedColor { get { return (Color?)GetValue(SelectedColorProperty); } set { SetValue(SelectedColorProperty, value); } } private static void OnSelectedColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ColorPicker colorPicker = (ColorPicker)d; if (colorPicker != null) colorPicker.OnSelectedColorChanged((Color?)e.OldValue, (Color?)e.NewValue); } private void OnSelectedColorChanged(Color? oldValue, Color? newValue) { SelectedColorText = GetFormatedColorString(newValue); RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(oldValue, newValue); args.RoutedEvent = ColorPicker.SelectedColorChangedEvent; RaiseEvent(args); } //public static readonly DependencyProperty SelectedBrushProperty = DependencyProperty.Register("SelectedBrush", typeof(Brush), typeof(ColorPicker), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnSelectedBrushPropertyChanged))); //public Brush SelectedBrush //{ // get // { // return (Brush)GetValue(SelectedBrushProperty); // } // set // { // SetValue(SelectedBrushProperty, value); // } //} //private static void OnSelectedBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) //{ // ColorPicker colorPicker = (ColorPicker)d; // if (colorPicker != null) // colorPicker.OnSelectedBrushChanged((Brush)e.OldValue, (Brush)e.NewValue); //} //private void OnSelectedBrushChanged(Brush oldValue, Brush newValue) //{ // if (newValue != null) // { // } //} #endregion //SelectedColor #region SelectedColorText public static readonly DependencyProperty SelectedColorTextProperty = DependencyProperty.Register("SelectedColorText", typeof(string), typeof(ColorPicker), new UIPropertyMetadata("")); public string SelectedColorText { get { return (string)GetValue(SelectedColorTextProperty); } protected set { SetValue(SelectedColorTextProperty, value); } } #endregion //SelectedColorText #region ShowTabHeaders public static readonly DependencyProperty ShowTabHeadersProperty = DependencyProperty.Register("ShowTabHeaders", typeof(bool), typeof(ColorPicker), new UIPropertyMetadata(true)); public bool ShowTabHeaders { get { return (bool)GetValue(ShowTabHeadersProperty); } set { SetValue(ShowTabHeadersProperty, value); } } #endregion //ShowTabHeaders #region ShowAvailableColors public static readonly DependencyProperty ShowAvailableColorsProperty = DependencyProperty.Register("ShowAvailableColors", typeof(bool), typeof(ColorPicker), new UIPropertyMetadata(true)); public bool ShowAvailableColors { get { return (bool)GetValue(ShowAvailableColorsProperty); } set { SetValue(ShowAvailableColorsProperty, value); } } #endregion //ShowAvailableColors #region ShowRecentColors public static readonly DependencyProperty ShowRecentColorsProperty = DependencyProperty.Register("ShowRecentColors", typeof(bool), typeof(ColorPicker), new UIPropertyMetadata(false)); public bool ShowRecentColors { get { return (bool)GetValue(ShowRecentColorsProperty); } set { SetValue(ShowRecentColorsProperty, value); } } #endregion //DisplayRecentColors #region ShowStandardColors public static readonly DependencyProperty ShowStandardColorsProperty = DependencyProperty.Register("ShowStandardColors", typeof(bool), typeof(ColorPicker), new UIPropertyMetadata(true)); public bool ShowStandardColors { get { return (bool)GetValue(ShowStandardColorsProperty); } set { SetValue(ShowStandardColorsProperty, value); } } #endregion //DisplayStandardColors #region ShowDropDownButton public static readonly DependencyProperty ShowDropDownButtonProperty = DependencyProperty.Register("ShowDropDownButton", typeof(bool), typeof(ColorPicker), new UIPropertyMetadata(true)); public bool ShowDropDownButton { get { return (bool)GetValue(ShowDropDownButtonProperty); } set { SetValue(ShowDropDownButtonProperty, value); } } #endregion //ShowDropDownButton #region StandardButtonHeader public static readonly DependencyProperty StandardButtonHeaderProperty = DependencyProperty.Register("StandardButtonHeader", typeof(string), typeof(ColorPicker), new UIPropertyMetadata("Standard")); public string StandardButtonHeader { get { return (string)GetValue(StandardButtonHeaderProperty); } set { SetValue(StandardButtonHeaderProperty, value); } } #endregion //StandardButtonHeader #region StandardColors public static readonly DependencyProperty StandardColorsProperty = DependencyProperty.Register("StandardColors", typeof(ObservableCollection), typeof(ColorPicker), new UIPropertyMetadata(CreateStandardColors())); public ObservableCollection StandardColors { get { return (ObservableCollection)GetValue(StandardColorsProperty); } set { SetValue(StandardColorsProperty, value); } } #endregion //StandardColors #region StandardColorsHeader public static readonly DependencyProperty StandardColorsHeaderProperty = DependencyProperty.Register("StandardColorsHeader", typeof(string), typeof(ColorPicker), new UIPropertyMetadata("Standard Colors")); public string StandardColorsHeader { get { return (string)GetValue(StandardColorsHeaderProperty); } set { SetValue(StandardColorsHeaderProperty, value); } } #endregion //StandardColorsHeader #region UsingAlphaChannel public static readonly DependencyProperty UsingAlphaChannelProperty = DependencyProperty.Register("UsingAlphaChannel", typeof(bool), typeof(ColorPicker), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnUsingAlphaChannelPropertyChanged))); public bool UsingAlphaChannel { get { return (bool)GetValue(UsingAlphaChannelProperty); } set { SetValue(UsingAlphaChannelProperty, value); } } private static void OnUsingAlphaChannelPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ColorPicker colorPicker = (ColorPicker)d; if (colorPicker != null) colorPicker.OnUsingAlphaChannelChanged(); } private void OnUsingAlphaChannelChanged() { SelectedColorText = GetFormatedColorString(SelectedColor); } #endregion //UsingAlphaChannel /// Identifies the dependency property. public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(object), typeof(ColorPicker)); /// /// Gets or sets the content of this control. /// public object Content { get => (object)this.GetValue(ContentProperty); set => this.SetValue(ContentProperty, value); } #endregion //Properties #region Constructors static ColorPicker() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorPicker), new FrameworkPropertyMetadata(typeof(ColorPicker))); } public ColorPicker() { #if VS2008 this.RecentColors = new ObservableCollection(); #else this.SetCurrentValue(ColorPicker.RecentColorsProperty, new ObservableCollection()); #endif Keyboard.AddKeyDownHandler(this, OnKeyDown); Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, OnMouseDownOutsideCapturedElement); } #endregion //Constructors #region Base Class Overrides public override void OnApplyTemplate() { base.OnApplyTemplate(); if (_availableColors != null) _availableColors.SelectionChanged -= Color_SelectionChanged; _availableColors = GetTemplateChild(PART_AvailableColors) as ListBox; if (_availableColors != null) _availableColors.SelectionChanged += Color_SelectionChanged; if (_standardColors != null) _standardColors.SelectionChanged -= Color_SelectionChanged; _standardColors = GetTemplateChild(PART_StandardColors) as ListBox; if (_standardColors != null) _standardColors.SelectionChanged += Color_SelectionChanged; if (_recentColors != null) _recentColors.SelectionChanged -= Color_SelectionChanged; _recentColors = GetTemplateChild(PART_RecentColors) as ListBox; if (_recentColors != null) _recentColors.SelectionChanged += Color_SelectionChanged; if (_popup != null) _popup.Opened -= Popup_Opened; _popup = GetTemplateChild(PART_ColorPickerPalettePopup) as Popup; if (_popup != null) _popup.Opened += Popup_Opened; _toggleButton = this.Template.FindName(PART_ColorPickerToggleButton, this) as ToggleButton; } protected override void OnMouseUp(MouseButtonEventArgs e) { base.OnMouseUp(e); // Close ColorPicker on MouseUp to prevent action of mouseUp on controls behind the ColorPicker. if (_selectionChanged) { CloseColorPicker(true); _selectionChanged = false; } } #endregion //Base Class Overrides #region Event Handlers private void OnKeyDown(object sender, KeyEventArgs e) { if (!IsOpen) { if (KeyboardUtilities.IsKeyModifyingPopupState(e)) { IsOpen = true; // Focus will be on ListBoxItem in Popup_Opened(). e.Handled = true; } } else { if (KeyboardUtilities.IsKeyModifyingPopupState(e)) { CloseColorPicker(true); e.Handled = true; } else if (e.Key == Key.Escape) { this.SelectedColor = _initialColor; CloseColorPicker(true); e.Handled = true; } } } private void OnMouseDownOutsideCapturedElement(object sender, MouseButtonEventArgs e) { CloseColorPicker(true); } private void Color_SelectionChanged(object sender, SelectionChangedEventArgs e) { ListBox lb = (ListBox)sender; if (e.AddedItems.Count > 0) { var colorItem = (ColorItem)e.AddedItems[0]; SelectedColor = colorItem.Color; if (!string.IsNullOrEmpty(colorItem.Name)) { this.SelectedColorText = colorItem.Name; } UpdateRecentColors(colorItem); _selectionChanged = true; lb.SelectedIndex = -1; //for now I don't care about keeping track of the selected color } } private void Popup_Opened(object sender, EventArgs e) { if ((_availableColors != null) && ShowAvailableColors) { FocusOnListBoxItem(_availableColors); } else if ((_standardColors != null) && ShowStandardColors) FocusOnListBoxItem(_standardColors); else if ((_recentColors != null) && ShowRecentColors) FocusOnListBoxItem(_recentColors); } private void FocusOnListBoxItem(ListBox listBox) { ListBoxItem listBoxItem = (ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(listBox.SelectedItem); if ((listBoxItem == null) && (listBox.Items.Count > 0)) listBoxItem = (ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(listBox.Items[0]); if (listBoxItem != null) listBoxItem.Focus(); } #endregion //Event Handlers #region Events #region SelectedColorChangedEvent public static readonly RoutedEvent SelectedColorChangedEvent = EventManager.RegisterRoutedEvent("SelectedColorChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(ColorPicker)); public event RoutedPropertyChangedEventHandler SelectedColorChanged { add { AddHandler(SelectedColorChangedEvent, value); } remove { RemoveHandler(SelectedColorChangedEvent, value); } } #endregion #region OpenedEvent public static readonly RoutedEvent OpenedEvent = EventManager.RegisterRoutedEvent("OpenedEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ColorPicker)); public event RoutedEventHandler Opened { add { AddHandler(OpenedEvent, value); } remove { RemoveHandler(OpenedEvent, value); } } #endregion //OpenedEvent #region ClosedEvent public static readonly RoutedEvent ClosedEvent = EventManager.RegisterRoutedEvent("ClosedEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ColorPicker)); public event RoutedEventHandler Closed { add { AddHandler(ClosedEvent, value); } remove { RemoveHandler(ClosedEvent, value); } } #endregion //ClosedEvent #endregion //Events #region Methods private void CloseColorPicker(bool isFocusOnColorPicker) { if (IsOpen) IsOpen = false; ReleaseMouseCapture(); if (isFocusOnColorPicker && (_toggleButton != null)) _toggleButton.Focus(); this.UpdateRecentColors(new ColorItem(SelectedColor, SelectedColorText)); } private void UpdateRecentColors(ColorItem colorItem) { if (!RecentColors.Contains(colorItem)) RecentColors.Add(colorItem); if (RecentColors.Count > 10) //don't allow more than ten, maybe make a property that can be set by the user. RecentColors.RemoveAt(0); } private string GetFormatedColorString(Color? colorToFormat) { if ((colorToFormat == null) || !colorToFormat.HasValue) return string.Empty; return ColorUtilities.FormatColorString(colorToFormat.Value.GetColorName(), UsingAlphaChannel); } private static ObservableCollection CreateStandardColors() { ObservableCollection standardColors = new ObservableCollection(); standardColors.Add(new ColorItem(Colors.Transparent, "Transparent")); standardColors.Add(new ColorItem(Colors.White, "White")); standardColors.Add(new ColorItem(Colors.Gray, "Gray")); standardColors.Add(new ColorItem(Colors.Black, "Black")); standardColors.Add(new ColorItem(Colors.Red, "Red")); standardColors.Add(new ColorItem(Colors.Green, "Green")); standardColors.Add(new ColorItem(Colors.Blue, "Blue")); standardColors.Add(new ColorItem(Colors.Yellow, "Yellow")); standardColors.Add(new ColorItem(Colors.Orange, "Orange")); standardColors.Add(new ColorItem(Colors.Purple, "Purple")); return standardColors; } private static ObservableCollection CreateAvailableColors() { ObservableCollection standardColors = new ObservableCollection(); foreach (var item in ColorUtilities.KnownColors) { if (!String.Equals(item.Key, "Transparent")) { var colorItem = new ColorItem(item.Value, item.Key); if (!standardColors.Contains(colorItem)) standardColors.Add(colorItem); } } return standardColors; } #endregion //Methods } public class KeyboardUtilities { public static bool IsKeyModifyingPopupState(KeyEventArgs e) { return ((((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) && ((e.SystemKey == Key.Down) || (e.SystemKey == Key.Up))) || (e.Key == Key.F4)); } } }