LoginSignup
3
5

More than 5 years have passed since last update.

WPF の ListBox と DataGrid で Dcitionary をバインドする

Last updated at Posted at 2019-03-10

WPF の ListBox と DataGrid で Dcitionary をバインドする

名前と値を Dictionary を使ってバインドしようとして少しはまって結果的に ViewModel に Dictionary の Value についての Binding 設定を一切書かずにすっきりできたのでメモ。

開発環境

  • Windows 10
  • Visual Studio 2017

ソリューションの拡張機能

  • Prism.Unity

新規プロジェクトの作成

Template Pack を参照ください。


RegionManagerにViewを登録

MainWindow に ContentView を登録

MainWindowViewModel.cs
        public MainWindowViewModel(IRegionManager regionManager)
        {
            regionManager.RegisterViewWithRegion("ContentRegion", typeof(ContentView));
        }

Person クラス を作成

Models フォルダを作り Person クラスを作成

Person.cs
    class Person
    {
        public string Name { get; set; }

        public string Sex { get; set; }

        public int Age { get; set; }
    }

ContentViewModel を ViewModels に作成

バインド用プロパティを定義してコンストラクタでサンプルデータ設定

ContentViewModel.cs
        public ContentViewModel()
        {
            this.PersonDictionaries = new Dictionary<string, Person>()
            {
                {"長男", new Person(){Name="Ichiro", Sex ="男", Age=10, } },
                {"次男", new Person(){Name="Jiro", Sex ="男", Age=9, } },
                {"三男", new Person(){Name="Saburo", Sex ="男", Age=8, } },
                {"四男", new Person(){Name="Shiro", Sex ="男", Age=7, } },
                {"長女", new Person(){Name="Goro", Sex ="男", Age=8, } },
            };

            this.Items = new Dictionary<string, Dictionary<string, string>>()
            {
                {"長男", new Dictionary<string, string>() {{"名前", "一郎"}, { "性別", "男" }, { "年齢", "20"}} },
                {"次男", new Dictionary<string, string>() {{"名前", "二郎"}, { "性別", "男" }, { "年齢", "19"}} },
                {"三男", new Dictionary<string, string>() {{"名前", "三郎"}, { "性別", "男" }, { "年齢", "18"}} },
                {"四男", new Dictionary<string, string>() {{"名前", "四郎"}, { "性別", "男" }, { "年齢", "17"}} },
                {"長女", new Dictionary<string, string>() {{"名前", "花子"}, { "性別", "女" }, { "年齢", "18"}} },
            };
        }

        private Dictionary<string, Person> _personDictionaries;

        public Dictionary<string, Person> PersonDictionaries
        {
            get { return _personDictionaries; }
            set { SetProperty(ref _personDictionaries, value); }
        }

        private Dictionary<string, Dictionary<string, string>> _Items;

        public Dictionary<string, Dictionary<string, string>> Items
        {
            get { return _Items; }
            set { SetProperty(ref _Items, value); }
        }

ContentView を Views に作成

ここで親データ(ListBox)と子データ(TextBlock or DataGrid)で直接バインド設定する

  • ListBox は x:Name で名前付けする

  • ListBox で Dictionary をバインドして、DisplayMenber に Key を設定して SelectedValuePath に Value を設定

  • Value は 名前付けした ListBox の SelectedValue を直接参照するため ViewModel 側でバインドプロパティを定義する必要がない

ContentView.xaml
<UserControl x:Class="PrismDictionaryBinding.Views.ContentView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListBox x:Name="lb1" ItemsSource="{Binding PersonDictionaries}" DisplayMemberPath="Key" SelectedValuePath="Value" />
        <Viewbox Grid.Column="1" >
            <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
                <TextBlock Text="{Binding ElementName=lb1, Path=SelectedValue.Name}"/>
                <TextBlock Text="{Binding ElementName=lb1, Path=SelectedValue.Sex}"/>
                <TextBlock Text="{Binding ElementName=lb1, Path=SelectedValue.Age}"/>
            </StackPanel>
        </Viewbox>
        <ListBox x:Name="lb2" Grid.Row="1" ItemsSource="{Binding Items}" DisplayMemberPath="Key" SelectedValuePath="Value" />
        <DataGrid Grid.Row="1" Grid.Column="1" ItemsSource="{Binding ElementName=lb2, Path=SelectedValue}" />
    </Grid>
</UserControl>

実行

上が Dictionary<string, person>

下が Dictionary<string, Dictionary<string, string>>

実行画面

まとめ

最初は SelectedItem をバインドしてそのプロパティセッターで対象データを表示しるということを ViewModel 側でやっていたんですが直接親コントロールとなる ListBox のプロパティを参照するようにしたことでシンプルに書くことが出来ました。

あと、SelectedItem の型を Dictionary にしていてエラーになってなぜかーと思っていたら KeyValuePair の存在をすっかり忘れていてハマりました…。

ソースは こちら に置いてあります。

3
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5