久しぶりのWPFネタです。小ネタ。
DataGridComboBoxColumnクラスを使うと、簡単にDataGridにComboBoxを設定できます。しかし、DataGridComboBoxColumnクラスのItemsSourceプロパティをBindingしようとすると、BindingのSourceがWindowのDataContextではなく、DataGridのItemsSourceに設定されたコレクションの行に該当するオブジェクトがSourceとして使用されます。そのため、WindowのDataContextが持っているコレクションを表示しようとするだけでも、Bindingが多少複雑になります。
AncestorTypeなどを使って親要素を辿ったりということを考えがちですが、もっと簡単なやり方があります。WindowのResourcesにCollectionViewSourceを使って、DataContextのコレクションをStaticResourceで参照出来るようにして、DataGridComboBoxColumnのItemsSourceにバインドする方法です。コード例を以下に示します。
まずDataGridの行に該当するクラスとして、以下のPersonクラスを定義します。このPersonクラスのParentIdをComboBoxから選択するという機能を実装します。
public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null) { field = value; var h = this.PropertyChanged; if (h != null) { h(this, new PropertyChangedEventArgs(propertyName)); } } private string id; public string Id { get { return this.id; } set { this.SetProperty(ref this.id, value); } } private string name; public string Name { get { return this.name; } set { this.SetProperty(ref this.name, value); } } private string parentId; public string ParentId { get { return this.parentId; } set { this.SetProperty(ref this.parentId, value); } } public Person() { this.Id = Guid.NewGuid().ToString(); } }
続けて、MainWindowのViewModelを作成します。今回は以下のようなPersonクラスのコレクションを持っただけのシンプルなものになります。
public class MainWindowViewModel { public ObservableCollection<Person> People { get; private set; } public MainWindowViewModel() { this.People = new ObservableCollection<Person>( Enumerable.Range(1, 100).Select(x => new Person { Name = "okazuki" + x })); } }
このクラスをMainWindowのDataContextに設定して、DataGridに表示します。そして、ParentIdプロパティをPeopleコレクションの中から選択出来るようにします。XAMLは以下のようになります。
<Window x:Class="MVVMDataGridComboboxColumnSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:l="clr-namespace:MVVMDataGridComboboxColumnSample" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <l:MainWindowViewModel /> </Window.DataContext> <Window.Resources> <!-- CollectionViewSourceで参照出来るようにしておいて --> <CollectionViewSource x:Key="PeopleSource" Source="{Binding People}" /> </Window.Resources> <Grid> <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding People}"> <DataGrid.Columns> <DataGridTextColumn Header="名前" Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <!-- DataGridComboBoxColumnのItemsSourceで使用する --> <DataGridComboBoxColumn Header="親" SelectedValuePath="Id" DisplayMemberPath="Name" ItemsSource="{Binding Source={StaticResource PeopleSource}}" SelectedValueBinding="{Binding ParentId}"/> </DataGrid.Columns> </DataGrid> </Grid> </Window>
WindowのResourcesにPeopleをCollectionViewSourceとして登録しているため、簡単にDataGridComboBoxColumnのItemsSourceが設定できています。実行結果を以下に示します。