WPF の ListBox と DataGrid で Dcitionary をバインドする
名前と値を Dictionary を使ってバインドしようとして少しはまって結果的に ViewModel に Dictionary の Value についての Binding 設定を一切書かずにすっきりできたのでメモ。
開発環境
- Windows 10
- Visual Studio 2017
ソリューションの拡張機能
- Prism.Unity
新規プロジェクトの作成
Template Pack を参照ください。
RegionManagerにViewを登録
MainWindow に ContentView を登録
public MainWindowViewModel(IRegionManager regionManager)
{
regionManager.RegisterViewWithRegion("ContentRegion", typeof(ContentView));
}
Person クラス を作成
Models フォルダを作り Person クラスを作成
class Person
{
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
}
ContentViewModel を ViewModels に作成
バインド用プロパティを定義してコンストラクタでサンプルデータ設定
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 側でバインドプロパティを定義する必要がない
<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 の存在をすっかり忘れていてハマりました…。
ソースは こちら に置いてあります。