C#のWPFで色選択コントロールを自作する その4

前回前々回前々前回に続いて、色を選択するコントロールを作成してみる

 

今度は本体のソース。

ColorPickerクラス

    public partial class ColorPicker : UserControl
    {
        private bool            IsPropertyChanging;
        private byte            FixedAlpha;
        private ColorPickerItem CustomItem;


        public static readonly DependencyProperty SelectedColorProperty =
            DependencyProperty.Register(
                "SelectedColor", // プロパティ名を指定
                typeof(Color), // プロパティの型を指定
                typeof(ColorPicker), // プロパティを所有する型を指定
                new FrameworkPropertyMetadata(Colors.White,
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                    (d, e) => {(d as ColorPicker).OnColorPropertyChanged(e); }));
        public Color SelectedColor
        {
            get { return (Color)GetValue(SelectedColorProperty); }
            set { SetValue(SelectedColorProperty, value); }
        }

        public event SelectionChangedEventHandler SelectionChanged;




        public ColorPicker()
        {
            InitializeComponent();

            float r, g, b;
            var   items = new List<ColorPickerItem>();

            for (int i = 0; i < 12; ++i)
            {
                ColorHelper.HSVtoRGB(0.0f, 0.0f, (float)i / 11.0f, out r, out g, out b);
                byte ba = 255;
                byte br = (byte)(r * 255.0f);
                byte bg = (byte)(g * 255.0f);
                byte bb = (byte)(b * 255.0f);
                items.Add(new ColorPickerItem()
                {
                    CategoryName = "グレースケール",
                    Column = i,
                    Row = 0,
                    ItemColor = Color.FromArgb(ba, br, bg, bb),
                });
            }
            for (int i = 0; i < 12; ++i)
            {
                ColorHelper.HSVtoRGB((float)i / 12.0f, 1.0f, 1.0f, out r, out g, out b);
                byte ba = 255;
                byte br = (byte)(r * 255.0f);
                byte bg = (byte)(g * 255.0f);
                byte bb = (byte)(b * 255.0f);
                items.Add(new ColorPickerItem()
                {
                    CategoryName = "基本の色",
                    Column = i,
                    Row = 1,
                    ItemColor = Color.FromArgb(ba, br, bg, bb),
                });
            }

            for (int j = 1; j < 5; ++j)
            {
                float s = (float)j / 5.0f;
                for (int i = 0; i < 12; ++i)
                {
                    ColorHelper.HSVtoRGB((float)i / 12.0f, s, 1.0f, out r, out g, out b);
                    byte ba = 255;
                    byte br = (byte)(r * 255.0f);
                    byte bg = (byte)(g * 255.0f);
                    byte bb = (byte)(b * 255.0f);
                    items.Add(new ColorPickerItem()
                    {
                        CategoryName = "その他の色",
                        Column = i,
                        Row = 2 + j - 1,
                        ItemColor = Color.FromArgb(ba, br, bg, bb),
                    });
                }
            }
            for (int j = 1; j < 4; ++j)
            {
                float v = 1.0f - (float)j / 5.0f;
                for (int i = 0; i < 12; ++i)
                {
                    ColorHelper.HSVtoRGB((float)i / 12.0f, 1.0f, v, out r, out g, out b);
                    byte ba = 255;
                    byte br = (byte)(r * 255.0f);
                    byte bg = (byte)(g * 255.0f);
                    byte bb = (byte)(b * 255.0f);
                    items.Add(new ColorPickerItem()
                    {
                        CategoryName = "その他の色",
                        Column = i,
                        Row = 6 + j - 1,
                        ItemColor = Color.FromArgb(ba, br, bg, bb),
                    });
                }
            }
            CustomItem = new ColorPickerItem()
            {
                CategoryName = "カスタム",
                Column = 0,
                Row = 9,
                ItemColor = Colors.Black,
            };
            items.Add(CustomItem);

            IsPropertyChanging = true;
            var src = new ListCollectionView(items);
            src.GroupDescriptions.Add(new PropertyGroupDescription("CategoryName"));
            Main.ItemsSource = src;

            FixedAlpha = SelectedColor.A;
            CustomItem.ItemColor = Color.FromArgb(255, SelectedColor.R, SelectedColor.G, SelectedColor.B);
            Main.SelectedItem = CustomItem;
            IsPropertyChanging = false;
        }

        private void Hyperlink_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new System.Windows.Forms.ColorDialog();
            dialog.FullOpen = true;
            dialog.Color = System.Drawing.Color.FromArgb(SelectedColor.A, SelectedColor.R, SelectedColor.G, SelectedColor.B);
            if (System.Windows.Forms.DialogResult.OK == dialog.ShowDialog())
            {
                SelectedColor = Color.FromArgb(FixedAlpha, dialog.Color.R, dialog.Color.G, dialog.Color.B);
            }
        }

        private void Main_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var item = Main.SelectedItem as ColorPickerItem;
            if (null == item) return;

            if (!IsPropertyChanging)
            {
                SelectedColor = Color.FromArgb(FixedAlpha, item.ItemColor.R, item.ItemColor.G, item.ItemColor.B);

                if (null != SelectionChanged)
                    SelectionChanged(this, e);
            }
        }

        public void OnColorPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            var item = Main.SelectedItem as ColorPickerItem;

            Color col = Colors.Black;
            col = (Color)e.NewValue;

            if (null != item)
                if (item.ItemColor == col) return;
            IsPropertyChanging = true;
            FixedAlpha = col.A;
            CustomItem.ItemColor = col;
            Main.SelectedItem = CustomItem;
            IsPropertyChanging = false;
        }
    }

 

SelectedColorプロパティ (8~20行目)

依存関係プロパティとして作成。

FrameworkPropertyMetadataOptionsでBindsTwoWayByDefaultフラグを立ててデフォルトでバインディングがTwoWayモードで動作するようにしている。 

 

 

SelectionChangedEventHandler (22、143~144行目)

SelectedColorで色を取得する以外に、選択が変更された時のイベントを処理出来るようにEventHandlerを実装。

 

 

データソースを作成 (31~115行目) 

GridのRow,Columnを指定しながら色毎のColorPickerItemを作成している。

色はHSV形式で数値を決めた後、RGBへ変換している。

HSVからRGBへの変換についてはこちらを参照

 

 

Hyperlink_Clickメソッド (124~133行目)

「その他の色...」がクリックされた時の動作を実装。

カラーダイアログを表示して、OKボタンが押されたらSelectedColorプロパティへ色をセット。

FullOpenプロパティをtrueにするとカスタムカラー作成用のコントロールが最初から見える状態で起動できる。

 

 

Main_SelectionChangedメソッド (134~146行目)

ComboBoxの選択が変更された時の処理。

SelectedColorプロパティの値のセットしている。

SelectedColorプロパティを変えるとOnColorPropertyChangedメソッドが呼び出されるが、

 そこでComboBoxのSelectedItemが変えられるとMain_SelectionChangedメソッドが呼ばれる

 ぐるぐる回ってしまわないようにIsPropertyChangingというフラグで制御している

 

 

OnColorPropertyChangedメソッド (148~162行目) 

依存関係プロパティによってSelectedColorの値が変更された時に呼び出される。(15行目)

指定された色をカスタム用のColorPickerItemへセットしそれをComboBoxのSelectedItemにする。

※SelectedItemを変えるとMain_SelectionChangedメソッドが呼び出されるが、

 そこでSelectedColorプロパティが変えられるとOnColorPropertyChangedメソッドが呼ばれる

 ぐるぐる回ってしまわないようにIsPropertyChangingというフラグで制御している