FC2ブログ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

DataGridのカラムを動的に作成する

 通常DataGridのカラム設定はXAMLで行います。
 カラムの設定では表示する値をデータバインディングで指定します。
 データバインディングしたプロパティがコレクションの場合はどうなるでしょうか?
 DataGridの自動カラム生成機能を利用して作成してみた結果が下の図です。
DataGrid自動生成
 カラムを自動生成した場合はヘッダー名はプロパティ名になります。
 右側の2つはLivetのViewModelクラスに実装されているプロパティですので今回は関係ありません。
 問題はValuesプロパティです。
 DataGridには(コレクション)とだけ表示されています。
 データテンプレートを使用して1つのセルに複数の値を表示することは可能ですが、コレクションの中身を展開してそれぞれ別カラムとして表示することはできません。
 今回の目的はコレクション(Values)の中身を展開し、それぞれ別カラムとして表示することです。
 いくつか方法はあるのでしょうが、この記事ではコードビハインド(C#)で動的にDataGridのカラムを定義します。

 まず表示するデータについて説明します。
 今回は例として以下の様なクラスを用意しました。
 DataGridのカラムを動的に作成するためのサンプルなので、ViewModelにModelの役割も持たせています。
using Livet;

namespace DataGridDynamicColumn.ViewModels
{
    public class DataViewModel : ViewModel
    {
        public DataViewModel()
        {
            Value = "";
            BackgroundColor = "";
        }

        #region Value変更通知プロパティ
        private string _Value;

        public string Value
        {
            get
            { return _Value; }
            set
            { 
                if (_Value == value)
                    return;
                _Value = value;
                RaisePropertyChanged();
            }
        }
        #endregion


        #region BackgroundColor変更通知プロパティ
        private string _BackgroundColor;

        public string BackgroundColor
        {
            get
            { return _BackgroundColor; }
            set
            { 
                if (_BackgroundColor == value)
                    return;
                _BackgroundColor = value;
                RaisePropertyChanged();
            }
        }
        #endregion

    }
}
 DataViewModelクラスは1件のデータを表すクラスです。
 値とDataGridで表示する際の背景色を持っています。
using Livet;
using System.Collections.ObjectModel;

namespace DataGridDynamicColumn.ViewModels
{
    public class ResultViewModel : ViewModel
    {
        public ResultViewModel(string name)
        {
            Name = name;
            Values = new ObservableCollection<DataViewModel>();
        }

        #region Name変更通知プロパティ
        private string _Name;

        public string Name
        {
            get
            { return _Name; }
            set
            { 
                if (_Name == value)
                    return;
                _Name = value;
                RaisePropertyChanged();
            }
        }
        #endregion


        public ObservableCollection<DataViewModel> Values { get; private set; }
    }
}
 ResultViewModelクラスは1つのテスト結果を保持するためのクラスです。
 テスト名を表すNameプロパティと各サンプルのテスト結果のコレクション(Values)を持っています。
using Livet;
using System.Collections.ObjectModel;

namespace DataGridDynamicColumn.ViewModels
{
    public class MainWindowViewModel : ViewModel
    {
        public MainWindowViewModel()
        {
            HeaderNames = new[] { "テスト名", "サンプル1", "サンプル2", "サンプル3" };
            Items = new ObservableCollection<ResultViewModel>();
            var result1 = new ResultViewModel("テスト1");
            result1.Values.Add(new DataViewModel() { Value = "A" });
            result1.Values.Add(new DataViewModel() { Value = "B" });
            result1.Values.Add(new DataViewModel() { Value = "C" });
            Items.Add(result1);

            var result2 = new ResultViewModel("テスト2");
            result2.Values.Add(new DataViewModel() { Value = "B" });
            result2.Values.Add(new DataViewModel() { Value = "D" });
            result2.Values.Add(new DataViewModel() { Value = "A" });
            Items.Add(result2);

            var result3 = new ResultViewModel("テスト3");
            result3.Values.Add(new DataViewModel() { Value = "C" });
            result3.Values.Add(new DataViewModel() { Value = "A" });
            result3.Values.Add(new DataViewModel() { Value = "A" });
            Items.Add(result3);
        }

        public string[] HeaderNames { get; private set; }

        public ObservableCollection<ResultViewModel> Items { get; private set; }

        // Valueの値を元に色分けする
        public void ColorCoding()
        {
            foreach(var result in Items)
            {
                foreach(var data in result.Values)
                {
                    switch (data.Value)
                    {
                        case "A":
                            data.BackgroundColor = "Green";
                            break;
                        case "B":
                            data.BackgroundColor = "LightGreen";
                            break;
                        case "D":
                            data.BackgroundColor = "Red";
                            break;
                        default:
                            break;
                    }
                }
            }
        }

    }
}
 MainWindowViewModelでは表示するデータを作成しています。
 ColorCodingメソッドではデータの値を使用して背景色を指定するメソッドです。
 次にViewを作成します。
<Window x:Class="DataGridDynamicColumn.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
        xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
        xmlns:v="clr-namespace:DataGridDynamicColumn.Views"
        xmlns:vm="clr-namespace:DataGridDynamicColumn.ViewModels"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    
    <i:Interaction.Triggers>
        <!--Windowが閉じたタイミングでViewModelのDisposeメソッドが呼ばれます-->
        <i:EventTrigger EventName="Closed">
            <l:DataContextDisposeAction/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    
    <Grid x:Name="RootGrid">
        <Grid.Resources>
            <Style x:Key="HeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                <Setter Property="TextBlock.TextAlignment" Value="Center"/>
            </Style>
            
            <Style x:Key="TextStyle" TargetType="{x:Type TextBlock}">
                <Setter Property="HorizontalAlignment" Value="Center"/>
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <DataGrid Grid.Row="0" Name="ResultDataGrid" ItemsSource="{Binding Items}" AutoGenerateColumns="False">
            
        </DataGrid>
        
        <Button Grid.Row="1" Content="色分けを行う">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="ColorCoding"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </Grid>
</Window>
 DataGridと色分けボタンがあるだけの簡単なViewです。
 今回、DataGridのカラム設定を動的に行うのでXAMLではカラム定義を行っていません。
 DataGridではItemsSourceのデータバインディングとAutoGenerateColumnsをOffにしているだけです。
 最後に、コードビハインドでDataGridのカラム設定を行います。
using DataGridDynamicColumn.ViewModels;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace DataGridDynamicColumn.Views
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InitializeDataGrid();
        }

        private void InitializeDataGrid()
        {
            var viewModel = (MainWindowViewModel)DataContext;

            // リソースからスタイルを取得
            var headerStyle = (Style)RootGrid.FindResource("HeaderStyle");
            var textStyle = (Style)RootGrid.FindResource("TextStyle");

            // テスト名カラムを設定
            ResultDataGrid.Columns.Add(new DataGridTextColumn()
            {
                Header = viewModel.HeaderNames.First(),
                HeaderStyle = headerStyle,
                ElementStyle = textStyle,
                IsReadOnly = true,
                CanUserSort = true,
                Binding = new Binding("Name")
            });

            // ResultViewModelのValuesの中身をカラムに設定する
            var headerNames = viewModel.HeaderNames.Skip(1).ToArray();
            for(var i = 0; i < headerNames.Length; i++)
            {
                // スタイルを作成
                var propertyBaseName = string.Format("Values[{0}]", i);
                var style = new Style(typeof(DataGridCell));
                style.Setters.Add(new Setter(
                    DataGridCell.BackgroundProperty, 
                    new Binding(propertyBaseName + ".BackgroundColor")));
                style.Setters.Add(new Setter(
                    DataGridCell.MarginProperty,
                    new Thickness(0)));

                // データ表示用カラムを作成
                ResultDataGrid.Columns.Add(new DataGridTextColumn()
                {
                    Header = headerNames[i],
                    HeaderStyle = headerStyle,
                    ElementStyle = textStyle,
                    IsReadOnly = true,
                    CanUserSort = true,
                    Binding = new Binding(propertyBaseName + ".Value"),
                    CellStyle = style
                });
            }
        }
    }
}
 DataGridのカラムを動的に作成するのは意外と簡単で、目的のDataGridのColumnsにデータグリッドカラムを追加するだけです。
 データグリッドカラムには幾つか種類がありますが、今回はテキスト表示するだけなのでDataGridTextColumnを使用しています。
 DataGridTextColumnのプロパティにヘッダー名やスタイル等を指定します。
 カラムのヘッダーを定義するためにはHeaderプロパティにヘッダーに表示するテキストを、HeaderStyleにヘッダーのスタイルを指定します。
 サンプルコードではXAMLでStyle定義を行い、FindResourceメソッドでそのスタイルを取得しています。
 スタイルを動的に作成する必要がない場合はこちらの方が書き易いと思います。
 IsReadOnlyプロパティで値が編集できるかどうかを指定できます。
 CanUseSortプロパティをtrueにするとDataGridのヘッダーをクリックするだけで表の並び替えが行えます。
 表示する値の定義はElementStyleプロパティ、CellStyleプロパティ、Bindingプロパティで行います。
 ElementStyleプロパティにはセル内に表示する要素のスタイルを指定します。
 CellStyleプロパティにはDataGridのセルのスタイルを指定します。(ヘッダーでは省略しています)
 Bindingプロパティで表示する値とデータバインディングを行います。
 Bindingクラスのコンストラクタにデータバインディングしたいプロパティ名のPathを指定するだけです。

 基本的にはこんな感じでカラム設定を行っていきます。
 Valuesの中身を展開し、それぞれ別カラムとして定義する場合もほぼ同じです。
 異なる点はデータバインディングするプロパティのパスを動的に作成して指定する必要がある点です。
 今回の場合、Valueプロパティのデータバインディングを行う場合はValues[0].Valueの様にPathを指定します。
 また、Styleでデータバインディングを使用したい場合はXAMLで定義することはできません。(プロパティのPathがValue[x].Valueの様になり、XAMLで指定できないので)
 データバインディングを使用したStyleを使用する場合はStyleも動的に作成する必要があります。
 Styleの定義はコンストラクタでデータタイプを指定します。
 今回作成するのはDataGridCellのスタイルなので型にDataGridCellを指定します。
 あとはSettersにスタイルの条件を追加していきます。
 Setterコンストラクタの第1引数にプロパティを、第2引数に値を指定します。
 データバインディングしたい場合は、第2引数にBindingインスタンスを指定します。

 このコードを実行すると以下の様になります。
動的DataGrid1
 目論見通りValuesの中身が展開されて別カラムとして表示されています。
 「色分けを行う」ボタンを押すと各データのBackgroundColorが指定され、セルの背景色が変化します。
動的DataGrid2
 今回はテキスト表示だけだったのでDataGridTextColumnを使用しましたが、DataGridCheckBoxColumn等を使用する場合も同じです。
 ただし、DataGridTemplateColumnは他と使い方が違う為、同じ方法ではうまくいきません。(DataGridTemplateColumnはBindingプロパティを持たないので)
 DataGridTemplateColumnを使用する場合は、Bindingではなくデータテンプレートでセルの内容を定義する必要があります。
 DataGridTemplateColumnは使ったことがないので説明は省略します。(Styleと同じ用にプロパティのPathに注意してデータテンプレートを作成すればうまくいくと思います)
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

カレンダー
09 | 2018/10 | 11
- 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 - - -
全記事表示リンク

全ての記事を表示する

カテゴリ
タグリスト

月別アーカイブ
11  09  08  07  06  05  04  03  02  01  12  11  10  09  08  07  06  04  03  02  01  12  11  10  09  08  07  06  05  04  03  02  01  12  11  10  09 
最新記事
リンク
最新コメント
検索フォーム
Amazon
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。