プログラミングを試してみたい人向けのC#プログラミングチュートリアル

[WPFアプリ]DataGridでの一覧表示

POINT
  • WPFアプリで一覧の表示方法をご紹介
  • 一覧はDataGridにDataTableをセットして表示する
  • CSVからデータを取得して一覧表示するサンプルを使って説明
目次

WPFアプリで一覧表示

今回はDataGridというオブジェクトを使用して一覧画面を製作します。
DataGridに表示するには、DataTableクラスで一覧表示するデータを用意します。
DataGridのDataContextにデータをセットしたDataTableクラスをセットすることでデータ表示ができます。

実行結果サンプル

CSV形式の家計簿データを読み込んで一覧表示するプログラムをサンプルとしてご紹介します。
実行すると、次のような画面を表示します。

プログラム実行時の画面

プログラム作成

ではプログラムを作成してみましょう。
次の5つのファイルをを同じフォルダに作成してください。

ファイル名 内容
Program.cs アプリケーションのメイン処理です。
MainWindowをメイン画面として起動します。
MainWindow.xaml メイン画面の画面レイアウトを定義します。
MainWindow.cs メイン画面の処理です。
CSVファイルを読み込んで一覧表示します。
data.csv 一覧表示するデータを保存したCSVファイルです。
Compile.bat コンパイルして実行ファイルを作成します。
SampleProgram.exeという実行ファイルを作成します。

Program.cs
using System;
using System.Windows;

namespace SampleProgram 
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            try
            {
                var app = new Application();
                app.Run(new MainWindow().getWindow());
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("正常な処理をすることができませんでした。\r\n{0}\r\n{1}", ex.Message, ex.StackTrace));
                return;
            }
        }
    }
}

今回、xamlファイルにミスがあった場合にエラーメッセージで内容確認ができるようにtry catchを入れています。
catch (Exception ex)でエラーが発生した場合に、ex.Message, ex.StackTraceと合わせてメッセージボックスで表示します。
これによりxamlファイルが読み込めない場合に、どこに問題があるかをメッセージ表示してくれます。

MainWindow.xaml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
	Title="SampleProgram" 
	Width="800"
	Height="500"
	WindowStartupLocation="CenterScreen"
	FontSize="13"
	FontFamily="メイリオ">
	
	<DockPanel 
		LastChildFill="True">
		
		<DataGrid Name="dgTableData"
			Margin="5"
			AutoGenerateColumns="False"
			ItemsSource="{Binding}"
			GridLinesVisibility="Vertical"
			VerticalGridLinesBrush="LightGray"
			HeadersVisibility="Column" 
			IsReadOnly="True" >
						
			<DataGrid.Columns>
				<DataGridTextColumn 
					Header="日付" 
					Width="*" 
					Binding="{Binding InoutDate}" />
				<DataGridTextColumn 
					Header="収支" 
					Width="*" 
					Binding="{Binding Inout}" />
				<DataGridTextColumn 
					Header="項目" 
					Width="*" 
					Binding="{Binding ItemName}" />
				<DataGridTextColumn 
					Header="収入" 
					Width="*" 
					Binding="{Binding Income, StringFormat=#\,0}" />
				<DataGridTextColumn 
					Header="支出" 
					Width="*" 
					Binding="{Binding Outgo, StringFormat=#\,0}" />
				<DataGridTextColumn 
					Header="メモ" 
					Width="*" 
					Binding="{Binding Memo}" />
			</DataGrid.Columns>
		</DataGrid>

	</DockPanel>

</Window>
MainWindow.cs
namespace SampleProgram 
{
    public class MainWindow
    {
        // 画面オブジェクト
        private System.Windows.Window _window;
        private System.Windows.Controls.DataGrid _dgTableData;

        // コンストラクタ
        public MainWindow()
        {
            // XAMLファイルを読み込んでフォーム生成
            string AppPath = System.AppDomain.CurrentDomain.BaseDirectory;
            using (var fs = new System.IO.FileStream(AppPath+"MainWindow.xaml", 
                System.IO.FileMode.Open, System.IO.FileAccess.Read))
            {
                _window = (System.Windows.Window)System.Windows.Markup.XamlReader.Load(fs);
            }
            
            // オブジェクト取得
            _dgTableData = (System.Windows.Controls.DataGrid)_window.FindName("dgTableData");
            
            // ファイルからデータを取得
            System.Data.DataTable dataTable = LoadDataFromCsvFile(AppPath+"data.csv");
            // 一覧に表示
            _dgTableData.DataContext = dataTable;

        }
        
        // Windowクラスを返す
        public System.Windows.Window getWindow()
        {
            return _window;
        }
                
        // ファイルからデータを取得
        private System.Data.DataTable LoadDataFromCsvFile(string fileName)
        {
            // データテーブル生成
            var dataTable = new System.Data.DataTable("Data");
            dataTable.Columns.Add("InoutDate", typeof(string));
            dataTable.Columns.Add("Inout", typeof(string));
            dataTable.Columns.Add("ItemName", typeof(string));
            dataTable.Columns.Add("Income", typeof(long));
            dataTable.Columns.Add("Outgo", typeof(long));
            dataTable.Columns.Add("Memo", typeof(string));

            // CSV読込み
            using(System.IO.StreamReader sr = new System.IO.StreamReader(@fileName, 
                System.Text.Encoding.GetEncoding("shift_jis")))
            {
                while (!sr.EndOfStream)
                {
                    string line = sr.ReadLine();
                    // CSVデータを解析
                    System.Data.DataRow row = CsvToDataRow(line, dataTable);
                    // 行追加
                    dataTable.Rows.Add(row);
                }
            }
            return dataTable;
        }
        
        // CSVデータを解析して行データとして返す
        private System.Data.DataRow CsvToDataRow(string line, System.Data.DataTable dataTable)
        {
            System.Data.DataRow row = dataTable.NewRow();
            
            // カンマ区切りで配列に分割
            string[] values = line.Split(',');
            for(int i=0; i < values.Length; i++)
            {
                switch (i)
                {
                  case 0:  // 日付
                      row["InoutDate"] = values[i];
                      break;
                  case 1:  // 収支
                      row["Inout"] = values[i];
                      break;
                  case 2:  // 項目
                      row["ItemName"] = values[i];
                      break;
                  case 3:  // 収入
                      row["Income"] = CsvDataToLongValue(values[i]);
                      break;
                  case 4:  // 支出
                      row["Outgo"] = CsvDataToLongValue(values[i]);
                      break;
                  case 5:  // メモ
                      row["Memo"] = values[i];
                      break;
                }
            }
            return row;
        }

        // CSVデータをlongの項目値にして返す
        private object CsvDataToLongValue(string value)
        {
            long lnum;
            if (long.TryParse(value, out lnum))
            {
                return lnum;
            } else {
                return System.DBNull.Value;
            }
        }
    }
}
data.csv
2024/02/01,収入,給与,1000000,,
2024/02/16,支出,住居費,,150000,
2024/02/16,支出,水道光熱費,,15000,
2024/02/16,支出,通信費,,10000,
2024/02/16,支出,食費,,70000,
2024/02/19,支出,保険料,,20000,
2024/02/25,支出,日用品費,,10000,
2024/02/25,支出,娯楽費,,50000,
Compile.bat
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe ^
  /t:winexe ^
  /r:"WPF\PresentationCore.dll" ^
  /r:"WPF\PresentationFramework.dll" ^
  /r:"WPF\WindowsBase.dll" ^
  /r:"System.Xaml.dll" ^
  /out:SampleProgram.exe ^
  /debug+ ^
  *.cs
pause

今回、Compile.batに/debug+のオプションを追加しています。
これによりコンパイルすると、デバッグ用データのSampleProgram.pdbも作成されます。
catch (Exception ex)でエラーが発生した場合に、ex.StackTraceを参照した際、コードの何行目でエラーになったかまで確認できます。
基本的な内容は、WPFアプリの作り方と同じです。
ファイルを作成できたらCompile.batを実行してみましょう。
同じフォルダにSampleProgram.exeが作成されます。
SampleProgram.exeを実行して実行結果サンプルのように動いたら成功です。

プログラム解説

作成したプログラムを解説していきます。

画面レイアウト

MainWindow.xamlから見ていきます。

MainWindow.xaml(抜粋)
	<DockPanel 
		LastChildFill="True">
		・・・
		<DataGrid Name="dgTableData"
			Margin="5"
		・・・
		</DataGrid>
	</DockPanel>

画面レイアウトには、DockPanelを使用しています。
DockPanelをLastChildFill="True"にして、その上にDataGridを配置します。
これにより画面サイズ一杯にDataGridが表示されます。
DataGridのMarginを指定することで、一覧の周りに少しスペースを空けています。

DataGridの設定

DataGridの設定を説明します。
今回は次のように設定しています。

MainWindow.xaml(抜粋)
		<DataGrid Name="dgTableData"
			Margin="5"
			AutoGenerateColumns="False"
			ItemsSource="{Binding}"
			CanUserAddRows="True" 
			GridLinesVisibility="Vertical"
			VerticalGridLinesBrush="LightGray"
			HeadersVisibility="Column" 
			IsReadOnly="True" >
		・・・
		</DataGrid>
プロパティ名 設定内容
Name 一覧のオブジェクト名です。
Margin 一覧の周りのスペースを指定しています。
AutoGenerateColumns 自動で列を作成するかの設定です。
今回はDataGrid.Columnsでデータと列の紐付けをするので、Falseにします。
ItemsSource データの紐付けを行いますよ、という意味で{Binding}を指定します。
GridLinesVisibility 一覧上の線表示の設定です。
今回は縦線のみ表示するように"Vertical"を指定しています。
VerticalGridLinesBrush 縦線の色を指定しています。
HeadersVisibility 列ヘッダのみ表示して、行ヘッダは非表示にするため"Column"を指定します。
IsReadOnly 今回は編集不可とするため"True"を指定します。

一覧表示処理

WPFアプリの基本的な処理は、WPFアプリの作り方を参照してください。
今回は一覧表示処理について解説します。
一覧表示処理は下記で実行しています。

MainWindow.cs(抜粋)
            string AppPath = System.AppDomain.CurrentDomain.BaseDirectory;
            ・・・
            // ファイルからデータを取得
            System.Data.DataTable dataTable = LoadDataFromCsvFile(AppPath+"data.csv");
            // 一覧に表示
            _dgTableData.DataContext = dataTable;

LoadDataFromCsvFileメソッドを呼び出して、DataTableを作成しています。
DataGridのDataContextに作成したDataTableをセットするとMainWindow.xamlの設定に従って一覧表示されます。

DataTableの作成

DataTableの作成は、次のコードで実行しています。

MainWindow.cs(抜粋)
            // データテーブル生成
            var dataTable = new System.Data.DataTable("Data");
            dataTable.Columns.Add("InoutDate", typeof(string));
            dataTable.Columns.Add("Inout", typeof(string));
            dataTable.Columns.Add("ItemName", typeof(string));
            dataTable.Columns.Add("Income", typeof(long));
            dataTable.Columns.Add("Outgo", typeof(long));
            dataTable.Columns.Add("Memo", typeof(string));

テーブル名は今回特に影響するところはないので"Data"としてDataTableの生成をしています。
Columns.Addで列の情報をセットします。
第1引数が列名、第2引数がtypeofでデータの型を渡します。
どの列に何の項目を表示するかは、xamlファイルの方で列定義として記載しています。

MainWindow.xaml(抜粋)
			<DataGrid.Columns>
				<DataGridTextColumn 
					Header="日付" 
					Width="*" 
					Binding="{Binding InoutDate}" />
				<DataGridTextColumn 
					Header="収支" 
					Width="*" 
					Binding="{Binding Inout}" />
				<DataGridTextColumn 
					Header="項目" 
					Width="*" 
					Binding="{Binding ItemName}" />
				<DataGridTextColumn 
					Header="収入" 
					Width="*" 
					Binding="{Binding Income, StringFormat=#\,0}" />
				<DataGridTextColumn 
					Header="支出" 
					Width="*" 
					Binding="{Binding Outgo, StringFormat=#\,0}" />
				<DataGridTextColumn 
					Header="メモ" 
					Width="*" 
					Binding="{Binding Memo}" />
			</DataGrid.Columns>
プロパティ名 設定内容
Header 列の表示名です。
Width 幅を指定します。
今回はすべて*にして均等な幅になるようにしています。
Binding ここでDataTableのどの列と紐付けるかを指定します。
収入と支出の列については表示形式も指定しています。

CSVデータの読込み

今回のサンプルでは、CSVファイルからデータを読み込むようにしています。
次のコードで、ファイルから1行ずつ読みだしています。
行ごとにCsvToDataRowメソッドを呼び出して行データを作成、DataTableの行データに追加しています。

MainWindow.cs(抜粋)
            // CSV読込み
            using(System.IO.StreamReader sr = new System.IO.StreamReader(@fileName, 
                System.Text.Encoding.GetEncoding("shift_jis")))
            {
                while (!sr.EndOfStream)
                {
                    string line = sr.ReadLine();
                    // CSVデータを解析
                    System.Data.DataRow row = CsvToDataRow(line, dataTable);
                    // 行追加
                    dataTable.Rows.Add(row);
                }
            }

CsvToDataRowでは、DataTableのNewRow()で行データのクラスを生成、データをセットしています。
line.Split(',')で、CSVの行ごとのデータに対し、カンマ区切りでの分割をしています。
Splitするとstring[](string型の配列)に分割することができます。
分割した各項目の値ごとにswitchで何番目のデータをどこにセットするかの処理を分けています。
各項目の値は、row["カラム名"]に対してセットします。

MainWindow.cs(抜粋)
        // CSVデータを解析して行データとして返す
        private System.Data.DataRow CsvToDataRow(string line, System.Data.DataTable dataTable)
        {
            System.Data.DataRow row = dataTable.NewRow();
            
            // カンマ区切りで配列に分割
            string[] values = line.Split(',');
            for(int i=0; i < values.Length; i++)
            {
                switch (i)
                {
                  case 0:  // 日付
                      row["InoutDate"] = values[i];
                      break;
                  case 1:  // 収支
                      row["Inout"] = values[i];
                      break;
                  case 2:  // 項目
                      row["ItemName"] = values[i];
                      break;
                  case 3:  // 収入
                      row["Income"] = CsvDataToLongValue(values[i]);
                      break;
                  case 4:  // 支出
                      row["Outgo"] = CsvDataToLongValue(values[i]);
                      break;
                  case 5:  // メモ
                      row["Memo"] = values[i];
                      break;
                }
            }
            return row;
        }

以上がDataGridでの一覧表示のご紹介になります。