ReactivePropertyでDataGridの列自動生成を試してみた
ReactivePropertyを使用してDataGridの列の自動生成機能を試した時のメモ
- 列自動生成機能を使用すると、パブリックなプロパティに対応した列が作成される
- プロパティの値が表示されるが、プロパティがReactivePropertyの場合はValueプロパティを表示するようにしたい
- AutoGeneratingColumnイベント発生時にReactivePropertyの場合はデータバインドにValueを使用し、Valueのデータ型に応じて列作成するように変更
参考ページ
列作成処理
■ ReactiveProperty用の列作成処理を定義
- プロパティがReactivePropertyの時に列を作成して返す
- データバインド時にValueを使用するように変更
- Valueのデータ型に応じて列を作成する
public static class ReactivePropertyUtility { public static bool TryCreateAutoColumn(string propertyName, Type propertyType, out DataGridColumn dataGridColumn) { dataGridColumn = null; // ReactivePropertyでない場合は処理終了 if (!(propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Codeplex.Reactive.ReactiveProperty<>))) return false; // Valueプロパティの情報を取得 var valuePropertyInfo = propertyType.GetProperty("Value"); if (valuePropertyInfo == null) return false; var valuePropertyType = valuePropertyInfo.PropertyType; // データ型に応じて列作成する DataGridComboBoxColumn comboBoxColumn = null; if (valuePropertyType.IsEnum) { comboBoxColumn = new DataGridComboBoxColumn(); comboBoxColumn.ItemsSource = Enum.GetValues(valuePropertyType); dataGridColumn = comboBoxColumn; } else if (typeof(string).IsAssignableFrom(valuePropertyType)) { dataGridColumn = new DataGridTextColumn(); } else if (typeof(bool).IsAssignableFrom(valuePropertyType)) { dataGridColumn = new DataGridCheckBoxColumn(); } else if (typeof(Uri).IsAssignableFrom(valuePropertyType)) { dataGridColumn = new DataGridHyperlinkColumn(); } else { dataGridColumn = new DataGridTextColumn(); } // 並び替え可能か設定 if (!typeof(IComparable).IsAssignableFrom(valuePropertyType)) { dataGridColumn.CanUserSort = false; } // ヘッダーの名称 dataGridColumn.Header = propertyName; // データバインドの設定、更新できない場合はOneWayを設定 DataGridBoundColumn boundColumn = dataGridColumn as DataGridBoundColumn; if (boundColumn != null || comboBoxColumn != null) { Binding binding = new Binding(propertyName + ".Value"); if (comboBoxColumn != null) { comboBoxColumn.SelectedItemBinding = binding; } else { boundColumn.Binding = binding; } if (valuePropertyInfo.CanWrite) binding.Mode = BindingMode.TwoWay; else binding.Mode = BindingMode.OneWay; } return true; } }
■ AutoGeneratingColumnイベント発生時に列を差し替える
// 列自動生成イベント this.dataGrid1.AutoGeneratingColumn += (sender, args) => { DataGridColumn column; // 列情報が作成できた場合に列を差し替える if (ReactivePropertyUtility.TryCreateAutoColumn(args.PropertyName, args.PropertyType, out column)) { // 差し替え args.Column = column; } };
コード全体
- MainViewModel.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections.ObjectModel; using System.Reactive.Linq; using Codeplex.Reactive; namespace DataGridSample1 { public class MainViewModel { public MainViewModel() { // テストデータを作成 this.DataGridData = new[] { new Record(false, "Name1", 20, GenderType.Male), new Record(true, "Name2", 30, GenderType.Female) } .ToObservable() .ToReadOnlyReactiveCollection(); } public ReadOnlyReactiveCollection<Record> DataGridData { get; private set; } } // 性別 public enum GenderType { Male, Female } // テストレコード用の定義クラス public class Record { public ReactiveProperty<bool> IsCheck { get; private set; } public ReactiveProperty<string> Name { get; private set; } public ReactiveProperty<int> Age { get; private set; } public ReactiveProperty<GenderType> Gender { get; private set; } public ReactiveProperty<long> AutoCountUp { get; private set; } public Record(bool check, string name, int age, GenderType gender) { this.IsCheck = new ReactiveProperty<bool>(check); this.Name = new ReactiveProperty<string>(name); this.Age = new ReactiveProperty<int>(age); this.Gender = new ReactiveProperty<GenderType>(gender); // 1秒間隔で値を更新 this.AutoCountUp = Observable.Interval(TimeSpan.FromSeconds(1)).ToReactiveProperty(); } } }
- MainWindow.xaml
<Window x:Class="DataGridSample1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:DataGridSample1" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <vm:MainViewModel></vm:MainViewModel> </Window.DataContext> <Grid> <DataGrid Name="dataGrid1" ItemsSource="{Binding DataGridData}"></DataGrid> </Grid> </Window>
- MainWindow.xaml.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace DataGridSample1 { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 列自動生成イベント this.dataGrid1.AutoGeneratingColumn += (sender, args) => { DataGridColumn column; // 列情報が作成できた場合に列を差し替える if (ReactivePropertyUtility.TryCreateAutoColumn(args.PropertyName, args.PropertyType, out column)) { // 差し替え args.Column = column; } }; } } public static class ReactivePropertyUtility { public static bool TryCreateAutoColumn(string propertyName, Type propertyType, out DataGridColumn dataGridColumn) { dataGridColumn = null; // ReactivePropertyでない場合は処理終了 if (!(propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Codeplex.Reactive.ReactiveProperty<>))) return false; // Valueプロパティの情報を取得 var valuePropertyInfo = propertyType.GetProperty("Value"); if (valuePropertyInfo == null) return false; var valuePropertyType = valuePropertyInfo.PropertyType; // データ型に応じて列作成する DataGridComboBoxColumn comboBoxColumn = null; if (valuePropertyType.IsEnum) { comboBoxColumn = new DataGridComboBoxColumn(); comboBoxColumn.ItemsSource = Enum.GetValues(valuePropertyType); dataGridColumn = comboBoxColumn; } else if (typeof(string).IsAssignableFrom(valuePropertyType)) { dataGridColumn = new DataGridTextColumn(); } else if (typeof(bool).IsAssignableFrom(valuePropertyType)) { dataGridColumn = new DataGridCheckBoxColumn(); } else if (typeof(Uri).IsAssignableFrom(valuePropertyType)) { dataGridColumn = new DataGridHyperlinkColumn(); } else { dataGridColumn = new DataGridTextColumn(); } // 並び替え可能か設定 if (!typeof(IComparable).IsAssignableFrom(valuePropertyType)) { dataGridColumn.CanUserSort = false; } // ヘッダーの名称 dataGridColumn.Header = propertyName; // データバインドの設定、更新できない場合はOneWayを設定 DataGridBoundColumn boundColumn = dataGridColumn as DataGridBoundColumn; if (boundColumn != null || comboBoxColumn != null) { Binding binding = new Binding(propertyName + ".Value"); if (comboBoxColumn != null) { comboBoxColumn.SelectedItemBinding = binding; } else { boundColumn.Binding = binding; } if (valuePropertyInfo.CanWrite) binding.Mode = BindingMode.TwoWay; else binding.Mode = BindingMode.OneWay; } return true; } } }