C#でDataGridに表示するデータを操作してみた

WPFのDataGridにバインディングするコレクションを操作して、DataGridの表示を操作してみました。

目次

  1. 環境
  2. 処理の流れ
  3. 試してみた
    1. XAML
    2. コード部
  4. 動かしてみた
    1. 使用したデータ
    2. 動かしてみた

環境

  • Visual Studio 2015 Express for Windows Desktop

処理の流れ

DataGridやListViewにコレクションをバインドする場合ですが、ObservableCollectionクラスのコレクションにすると便利です。ObservableCollectionのインスタンスに登録したデータを変更すると、バインドしたコントロールに変更通知が送られて、コントロールの表示も変更されるからです。

  1. 基になるデータをデータベースからDataTableやCollectionに読み込む。

  2. レコードに含まれるフィールドを定義したクラスを作る。

  3. 定義したクラスの型のObservableCollectionクラスのインスタンスを作る。

  4. ObservableCollectionクラスのインスタンスを、表示用のコントロール(DataGridやListView)にバインドする。

  5. LINQを使ってDataTableやCollectionから抽出や結合したデータを定義したレコードクラスのインスタンスにして、ObservableCollectionのインスタンスに追加する。

フロー

何というか、言葉ではわかりにくいですね。試してみましょう。

試してみた

System.Data.SQLiteを使ってSQLite3のデータを表示するアプリを作ってみます。データベースには2つのテーブル(短歌テーブルと歌人テーブル)があり、それぞれのテーブルの内容と2つのテーブルを結合した結果を表示します。表示の内容は、ボタンによって切り替えます。

XAML

XAMLは下記にしました。

<Window x:Class="datagrid_trial.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:datagrid_trial"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="80" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="50" />
            <RowDefinition Height="50" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <DataGrid Name="datagrid" ItemsSource="{Binding}" Grid.Column="0" Grid.Row="0" Grid.RowSpan="4"
                  AlternationCount="2"
                  AlternatingRowBackground="AliceBlue"
                  GridLinesVisibility="Vertical"
                  VerticalGridLinesBrush="LightGray"
                  CanUserAddRows="False"
                  CanUserDeleteRows="False" SelectionUnit="Cell"/>
        <Button Name="buttonPoem" Content="短歌" Grid.Column="1" Grid.Row="0" Margin="5" Click="buttonPoem_Click" />
        <Button Name="buttonPoet" Content="歌人" Grid.Column="1" Grid.Row="1" Margin="5" Click="buttonPoet_Click" />
        <Button Name="buttonMarge" Content="短歌と歌人" Grid.Column="1" Grid.Row="2" Margin="5" Click="buttonMarge_Click" />
    </Grid>
</Window>

DataGrid要素に ItemsSource=”{Binding}”として、バインドしたものを表示するように設定します。

コード部

コードは下記の様にしました。

using System;
using System.Linq;
using System.Windows;
using System.Data;
using System.Data.Common;
using System.Collections.ObjectModel;

namespace datagrid_trial
{
    public partial class MainWindow : Window
    {
        // データを保持するDataTableの作成
        DataTable poemTable = new DataTable();
        DataTable poetTable = new DataTable();

        ObservableCollection<ViewData> ViewCollection = new ObservableCollection<ViewData>();

        public class ViewData
        {
            public string Field1 { get; set; }
            public string Field2 { get; set; }
            public string Field3 { get; set; }
        }

        public MainWindow()
        {
            InitializeComponent();

            ReadDataFromDatabaseToDataTable();

            datagrid.DataContext = ViewCollection;
        }

        private void ReadDataFromDatabaseToDataTable()
        {
            // データの読み込み
            try
            {
                DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SQLite");
                using (DbConnection connection = factory.CreateConnection())
                {
                    connection.ConnectionString = "Data Source=poemData.db"; // ファイル名を指定する
                    using (connection)
                    {
                        // コマンドを作る
                        DbCommand command = connection.CreateCommand();
                        command.CommandText = "SELECT * FROM poem";
                        command.CommandType = CommandType.Text;
                        command.Connection = connection;

                        // DataAdapterを作る
                        DbDataAdapter adapter = factory.CreateDataAdapter();
                        adapter.SelectCommand = command;

                        // 読み込み
                        adapter.Fill(poemTable);

                        // コマンドを作る
                        DbCommand command2 = connection.CreateCommand();
                        command2.CommandText = "SELECT * FROM poet_master";
                        command2.CommandType = CommandType.Text;
                        command2.Connection = connection;

                        // DataAdapterを作る
                        DbDataAdapter adapter2 = factory.CreateDataAdapter();
                        adapter2.SelectCommand = command2;

                        // 読み込み
                        adapter2.Fill(poetTable);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void buttonPoem_Click(object sender, RoutedEventArgs e)
        {
            ViewCollection.Clear();

            var query = from r in poemTable.AsEnumerable() select r;
            foreach (var r in query)
            {
                ViewData v = new ViewData();
                v.Field1 = r.Field<Int64>("id").ToString();
                v.Field2 = r.Field<string>("body");
                v.Field3 = r.Field<Int64>("poet").ToString();
                ViewCollection.Add(v);
            }
            datagrid.Columns[0].Header = "ID";
            datagrid.Columns[1].Header = "短歌";
            datagrid.Columns[2].Header = "詠み人ID";
        }

        private void buttonPoet_Click(object sender, RoutedEventArgs e)
        {
            ViewCollection.Clear();

            var query = from r in poetTable.AsEnumerable() select r;
            foreach(var r in query)
            {
                ViewData v = new ViewData();
                v.Field1 = r.Field<Int64>("id").ToString();
                v.Field2 = r.Field<string>("name");
                ViewCollection.Add(v);
            }
            datagrid.Columns[0].Header = "ID";
            datagrid.Columns[1].Header = "歌人";
            datagrid.Columns[2].Header = "";
        }

        private void buttonMarge_Click(object sender, RoutedEventArgs e)
        {
            ViewCollection.Clear();

            // 結合を実行する
            var query =
                from p in poemTable.AsEnumerable()
                join q in poetTable.AsEnumerable()
                on p.Field<Int64>("poet") equals q.Field<Int64>("id")
                select new
                {
                    body = p.Field<string>("body"),
                    poet = q.Field<string>("name")
                };
            foreach (var s in query)
            {
                ViewData v = new ViewData();
                v.Field1 = s.body;
                v.Field2 = s.poet;
                ViewCollection.Add(v);
            }

            datagrid.Columns[0].Header = "短歌";
            datagrid.Columns[1].Header = "歌人";
            datagrid.Columns[2].Header = "";
        }
    }
}

部分毎に解説します。

まず、コードには現れませんが、最初にNuGetでSystem.Data.SQLiteパッケージを追加しておきます。

using System;
using System.Linq;
using System.Windows;
using System.Data;
using System.Data.Common;
using System.Collections.ObjectModel;

using System.Dataとusing System.Data.Commonは、ADO.Net(DbProviderFactory)でデータを読み込むためのものです。System.Collections.ObjectModelはObservableCollectionクラスを使用するためのものです。

private void ReadDataFromDatabaseToDataTable()
{
    // 中略
}

このメソッドでデータベースからデータをDataTableに読み込みます。

DataTable poemTable = new DataTable();
DataTable poetTable = new DataTable();

ObservableCollection<ViewData> ViewCollection = new ObservableCollection<ViewData>();

public class ViewData
{
    public string Field1 { get; set; }
    public string Field2 { get; set; }
    public string Field3 { get; set; }
}

ViewDataというクラスは、ObservableCollectionに入れるデータの型を表すクラスです。

ObservableCollection<ViewData> ViewCollection = new ObservableCollection<ViewData>()でViewData型のObservableCollectionのインスタンス(ViewCollection)を作ります。

このViewCollectionをDataGridにバインドすると、ViewDataクラスの各プロパティがDataGridのカラムになります。

private void buttonMarge_Click(object sender, RoutedEventArgs e)
{
    ViewCollection.Clear();

    // 結合を実行する
    var query =
        from p in poemTable.AsEnumerable()
        join q in poetTable.AsEnumerable()
        on p.Field<Int64>("poet") equals q.Field<Int64>("id")
        select new
        {
            body = p.Field<string>("body"),
            poet = q.Field<string>("name")
        };
    foreach (var s in query)
    {
        ViewData v = new ViewData();
        v.Field1 = s.body;
        v.Field2 = s.poet;
        ViewCollection.Add(v);
    }

    datagrid.Columns[0].Header = "短歌";
    datagrid.Columns[1].Header = "歌人";
    datagrid.Columns[2].Header = "";
}

ボタンをクリックしたときの処理は似てますので、1つだけ説明します。上記のボタンでは、2つのDataTableを結合した結果をObservableCollectionに入れています。最初にViewCollection.Clear()で既存のObservableCollectionインスタンス内のデータを全部削除します。queryにLINQのクエリを設定してforeachでLINQクエリを実行します。foreach内でViewDataクラスのインスタンスを作ってObservableCollectionインスタンスにAddします。

DataGrid.Columnsは、DataGridのカラムの情報のコレクションです。このコレクションの各要素にHeaderプロパティがあって、そのHeaderプロパティに文字列を代入すると、そのカラムの列タイトルになります。

「DataGridにバインドしたObservableCollectionインスタンスをClearした後にLINQの結果をAddする」という処理を、各ボタンのコードに書くわけです。

動かしてみた

使用したデータ

SQLite3データベース内の2つのテーブルの内容は下記にしました。

poemテーブルの中身は下記です。

id

body

poet

1

あかねさす紫野行き標野行き野守は見ずや君が袖振る

2

2

あしひきの山川の瀬の鳴るなへに弓月が岳に雲立ち渡る

1

3

うらうらに照れる春日にひばり上がり心悲しも独し思へば

15

4

うらさぶる心さまねしひさかたの天のしぐれの流らふ見れば

18

5

たまきはる宇智の大野に馬並めて朝踏ますらむその草深野

17

6

ぬばたまの夜の更けゆけば久木生ふる清き川原に千鳥しば鳴く

9

7

ひさかたの天の香具山この夕霞たなびく春立つらしも

1

8

み吉野の象山の際の木末にはここだも騒く鳥の声かも

9

9

わたつみの豊旗雲に入り日差し今夜の月夜清く照りこそ

19

10

葦辺行く鴨の羽交ひに霜降りて寒き夕は大和し思ほゆ

11

poet_masterテーブルの中身は下記です。

id

name

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

舒明天皇

poemテーブルのpoetフィールドとpoet_masterテーブルのidがリレーションします。

動かしてみた

ビルドして実行すると下図のようになります。各ボタンを押した状態です。

実行結果(短歌) 実行結果(歌人) 実行結果(両方)

ObservableCollectionインスタンスの中身を変えるだけで表示が変わりました。

公開日

広告