プログラミング勉強ノート

プログラミングで勉強した事を書いていきます。

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();
        }
    }
}


<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;
        }
    }
}