午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPFの色一覧を取得してComboBoxにBindingで表示、一覧はBrushesからPropertyInfoとGetPropertiesを使って取得

動作の様子

Animation20220616_153227.gif
できた

コード

2022WPF/20220616_ComboBox_Colors_Binding/20220616_ComboBox_Colors_Binding at master · gogowaten/2022WPF

github.com


MainWindow.xaml

<Window x:Class="_20220616_ComboBox_Colors_Binding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_20220616_ComboBox_Colors_Binding"
        mc:Ignorable="d"
        Title="MainWindow" Height="500" Width="400">
  <Grid>
    <StackPanel x:Name="MyStackPanel">
      <StackPanel.Resources>
        <Style TargetType="TextBlock">
          <Setter Property="Margin" Value="0,10,0,0"/>
        </Style>
      </StackPanel.Resources>      

      <TextBlock Text="色だけ表示"/>
      <ComboBox ItemsSource="{Binding}">
        <ComboBox.ItemTemplate>
          <DataTemplate>
            <Ellipse Fill="{Binding Value}" Width="20" Height="20"/>
          </DataTemplate>
        </ComboBox.ItemTemplate>
      </ComboBox>
      
      <TextBlock Text="色と色名を表示"/>
      <ComboBox ItemsSource="{Binding}">
        <ComboBox.ItemTemplate>
          <DataTemplate>
            <StackPanel Orientation="Horizontal">
              <Border Width="20" Background="{Binding Value}" Margin="0,2,4,2"/>
              <TextBlock Text="{Binding Key}"/>
            </StackPanel>
          </DataTemplate>
        </ComboBox.ItemTemplate>
      </ComboBox>

      <TextBox x:Name="MyTextBox1" Text="ComboBoxとBinding"
               FontSize="18" Margin="20,20,20,0"/>
      <TextBlock Text="TextBoxのForegroundとBinding"/>
      <ComboBox ItemsSource="{Binding}"
                SelectedValuePath="Value"
                SelectedValue="{Binding ElementName=MyTextBox1, Path=Foreground}">
        <ComboBox.ItemTemplate>
          <DataTemplate>
            <StackPanel Orientation="Horizontal">
              <Border Width="20" Background="{Binding Value}" Margin="0,2,4,2"/>
              <TextBlock Text="{Binding Key}"/>
            </StackPanel>
          </DataTemplate>
        </ComboBox.ItemTemplate>
      </ComboBox>
      
      <TextBlock Text="TextBoxのBackgroundとBinding"/>
      <ComboBox ItemsSource="{Binding}"
                SelectedValuePath="Value"
                SelectedValue="{Binding ElementName=MyTextBox1, Path=Background}">
        <ComboBox.ItemTemplate>
          <DataTemplate>
            <StackPanel Orientation="Horizontal">
              <Border Width="20" Background="{Binding Value}" Margin="0,2,4,2"/>
              <TextBlock Text="{Binding Key}"/>
            </StackPanel>
          </DataTemplate>
        </ComboBox.ItemTemplate>
      </ComboBox>

      <Separator Background="{Binding ElementName=MyComboBox3, Path=SelectedValue}"
                 Margin="20,20,20,0"/>
      <TextBlock Text="多数要素とComboBoxのSelectedValueをBinding"/>
      <ComboBox x:Name="MyComboBox3"
                ItemsSource="{Binding}"
                SelectedValuePath="Value">
        <ComboBox.ItemTemplate>
          <DataTemplate>
            <StackPanel Orientation="Horizontal">
              <Border Width="20" Background="{Binding Value}" Margin="0,2,4,2"/>
              <TextBlock Text="{Binding Key}"/>
            </StackPanel>
          </DataTemplate>
        </ComboBox.ItemTemplate>
      </ComboBox>
      <TextBlock Text="textblock" 
                 Foreground="{Binding ElementName=MyComboBox3, Path=SelectedValue}"/>
      <Ellipse Width="80" Height="80"
               Fill="{Binding ElementName=MyComboBox3,Path=SelectedValue}"/>

    </StackPanel>
  </Grid>
</Window>



MainWindow.xaml.cs

using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;

namespace _20220616_ComboBox_Colors_Binding
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Dictionary<string, Brush>? dict = MakeBrushesDictionary();
            MyStackPanel.DataContext = dict;
        }

        /// <summary>
        /// BrushesのBrushと名前一覧を取得
        /// </summary>
        /// <returns></returns>
        private static Dictionary<string, Brush> MakeBrushesDictionary()
        {
            //Brushesの情報(PublicとStaticなプロパティ全部)取得
            System.Reflection.PropertyInfo[]? brushInfos =
                typeof(Brushes).GetProperties(
                    System.Reflection.BindingFlags.Public |
                    System.Reflection.BindingFlags.Static);

            Dictionary<string, Brush> dict = new();
            foreach (var item in brushInfos)
            {
                if (item.GetValue(null) is not Brush b)
                {
                    continue;
                }
                dict.Add(item.Name, b);
            }
            return dict;
        }

    }
}



一覧作成
Brushesから取得した色の名前とBrushの一覧を詰め込んだDictionaryを作成するMakeBrushesDictionary
Dictionaryの中の要素を見てみると
KeyとValue
KeyプロパティにはString型で色の名前と、ValueプロパティにはBrush型の値が入っている

このDictionaryをComboBoxのItemsSourceにBindingすればいい
ここではXAMLで書いておいたStackPanelのMyStackPanelのDataContextに指定した、14行目
あとはXAMLのほうで設定する

色の一覧を表示するだけのComboBox
一覧データのDictionaryはMyStackPanelのDataContextに指定したので、MyStackPanelの中のComboBoxにも届いているから、ItemsSourceをBindingに指定すれば完了
あとはデータの要素1件1件の表示の仕方を設定はItemTemplate、今回はEllipse要素で丸を表示するようにした、21行目、塗りつぶしのFillにValueをBindingしている、このValueはDictionaryのValueプロパティのことなので、それに入れておいたBrushがBindingされる
結果
BindingしたBrushでFillしたEllipse
やぶからスティックなルー語はおっさんの嗜み

色名と色を表示するComboBox
さっきの色だけのComboBoxに色名を足すだけなので、色名表示用のTextBlockを32行目に付け足しただけ、そのBindingのPathに指定しているKeyはDictionaryのKeyのこと
これで
色と色名表示
これで表示はだいたいできた
次はComboBoxで選択した項目と、なにか別の要素とのBinding


TextBoxのForeground(文字色)とBinding

TextBoxのForegroundとBinding
こうしたいときは
こう
TextBoxのForegroundとBinding
さっきの色と色名を表示するだけのComboBoxと違うのは、42と43行目だけ
43行目は普通のBindingで選択された値SelectedValueとTextBoxのBinding
42行目ではComboBoxのSelectedValuePathにValueを指定している、これは選択された項目自体はDictionaryの要素の1つで、その要素はKeyとValueの2つの値が入っている、で、そのどちらの値とBindingさせるのかってのを指定する必要があってValueを指定しているみたい

TextBoxのBackground(背景色)とBinding

TextBoxのBackgroundとBinding
さっきのForegroundがBackgroundに変わっただけ

ここまではTextBoxが軸、元、SourceでBindingしているけど、BindingのBindingModeが双方向(たぶん既定値)なのでComboBoxの選択項目を変更してもTextBoxのほうも変化している

今度は逆にComboBoxで選択された項目を軸にBinding
SeparatorのBackground
TextblockのForeground
EllipseのFill
この3つ同時

多数要素とBinding
結果の動作はさっきと同じでComboBoxの項目を変更すると色が変わるのは、これも双方向Bindingだからみたい
ComboBoxの設定ではSelectedValuePathにValueの指定が必要
各要素のBindingのPathにはSelectedValueを指定、似たプロパティでSelectedItemってのがあるけど、これだと色が変わらなかった、Bindingにならない

これ思ったけど、一覧表示で色名いらなくて色だけでいいなら、Dictionaryの代わりに配列やListをComboBoxのItemsSourceにBindingすれば、SelectedValuePathは必要ない?
試したら↓でできた

色名表示いらないならこれでいい
1種類の値なら配列やListをItemsSourceにBindingする、この場合はSelectedValuePathは必要ない
必要になるのは1要素にKeyとValueの2つのプロパティを持つDictionary型や、もっと多くのプロパティを持つ要素の配列やListのときってことかな


まつざきしげるいろ


関連記事

次回のWPF記事

gogowaten.hatenablog.com

前回のWPF記事は昨日
gogowaten.hatenablog.com

3年前
WPFのListBoxでいろいろ、Binding、見た目の変更、横リスト - 午後わてんのブログ
gogowaten.hatenablog.com
このときのとほとんど同じ