はじめに
前回は WPF で MVVM パターンによる構成でコンボボックスを使用する方法について紹介しました。
今回の記事では、同じく MVVM パターンによる構成で DataGrid を使用する方法について紹介していきます。
最終的な成果物のイメージです。
MVVM に関しては下記の記事で紹介しているので、興味のある方は見てみて下さい。
開発環境
- .NET6
- Visual Studio 2022(v17.6.4)
ファイル構成
ファイル構成は以下の用になっています。
ダミーデータとして CSV を使用します。
SQLServer に接続するようなクラスがありますが、中身はインターフェースを実装しただけのクラスになっています。今回は特に使用しません。
using DataGrid_Sample.Enitities;
using DataGrid_Sample.Repositories;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace DataGrid_Sample.SQL
{
public sealed class SQLServerClient : IDataGridRepository
{
public SQLServerClient()
{
// サーバーに接続する処理など
}
public ObservableCollection<UserEnitity> Get()
{
// 未実装の例外はそのままにしておく
throw new NotImplementedException();
}
}
}
ダミーデータは前回と同じく個人情報テストデータジェネレーターというサービスを利用して作成しました。
画面の実装
MainWindow.xaml
を以下のように記述します。
<Window
x:Class="DataGrid_Sample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Behaviors="http://schemas.microsoft.com/xaml/behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DataGrid_Sample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="DataGrid-Sample"
Width="500"
Height="450"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="300" />
<RowDefinition />
</Grid.RowDefinitions>
<Label
Grid.Row="0"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Content="DataGridテスト"
FontSize="26" />
<DataGrid
x:Name="dataGridForUserData"
Grid.Row="1"
d:ItemsSource="{d:SampleData ItemCount=5}"
AutoGenerateColumns="False"
Cursor="Hand"
GridLinesVisibility="All"
IsReadOnly="True"
ItemsSource="{Binding UserEntityData}"
VerticalScrollBarVisibility="Auto">
<Behaviors:Interaction.Triggers>
<Behaviors:EventTrigger EventName="SelectionChanged">
<Behaviors:InvokeCommandAction Command="{Binding ExecuteRowSelectedCommand}" CommandParameter="{Binding ElementName=dataGridForUserData, Path=SelectedItem}" />
</Behaviors:EventTrigger>
</Behaviors:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn
Width="80"
Binding="{Binding Id}"
FontSize="20"
Header="ID" />
<DataGridTextColumn
Binding="{Binding Name}"
FontSize="20"
Header="名前" />
<DataGridTextColumn
Binding="{Binding Age}"
FontSize="20"
Header="年齢" />
<DataGridTextColumn
Binding="{Binding BrithDay}"
FontSize="20"
Header="誕生日" />
<DataGridTextColumn
Binding="{Binding Sex}"
FontSize="20"
Header="性別" />
<DataGridTextColumn
Binding="{Binding Email}"
FontSize="20"
Header="Email" />
<DataGridTextColumn
Binding="{Binding Tell}"
FontSize="20"
Header="電話番号" />
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<Button
Width="150"
Height="40"
Margin="10,0,0,0"
Command="{Binding ExecuteGetData}"
Content="データを取得する"
Cursor="Hand" />
</StackPanel>
</Grid>
</Window>
コードの内容について解説していきます。
ItemsSource
DataGrid に表示させるデータはItemsSource
プロパティに指定します。ここでは、ItemsSource="{Binding UserEntityData}"
と記述しUserEntityData
という ViewModel 側のプロパティをデータバインディングしています。
その実態はUserEnitity
クラスで、CSV やデータベースから取得してきたデータを格納するクラスです。
using System;
namespace DataGrid_Sample.Enitities
{
public sealed class UserEnitity
{
public int Id { get; }
public string Name { get; }
public int Age { get; }
public string BrithDay { get; }
public string Sex { get; }
public string Email { get; }
public string Tell { get; }
public UserEnitity(int id, string name, string age, string brithDay, string sex, string email, string tell)
{
Id = id;
Name = name;
Age = Int32.Parse(age);
BrithDay = brithDay;
Sex = sex;
Email = email;
Tell = tell;
}
}
}
引数に指定している値はValueObject
としてクラスに変換してプロパティに格納したほうが、振る舞いを持たせたい(値を表示する際にロジックを持たせたい)ときにロジックが散らばったりしないのですが、今回は簡易に実装するためプリミティブな形のまま格納します。
DataGrid のItemsSource
プロパティに指定できるデータはIEnumerable
が実装されたクラスを指定する必要があるので、UserEnitity
クラスをObservableCollection<T>
に格納します。
IEnumerable
はざっくり言うと、foreach
で反復して出力できる型のことです。
ObservableCollection<T>
はIEnumerable
が実装されたクラスです。
/// <summary>
/// DataGridに表示させるリスト
/// </summary>
public ObservableCollection<UserEnitity> UserEntityData
{
get => _userEntityData;
set => SetProperty(ref _userEntityData, value);
}
private ObservableCollection<UserEnitity> _userEntityData;
DataGrid コントロールのプロパティにどのようなデータが指定できるかは、実際にコードを書いてみると把握できます。
AutoGenerateColumns=“False”
AutoGenerateColumns="False"
を指定することで、DataGrid はデータソースから自動的に列を生成しません。
つまり、この属性を指定した場合は<DataGrid.Columns>
と指定して、出力するカラムとItemsSource
に指定したクラスのプロパティを指定する必要があります。
AutoGenerateColumns="False"
や<DataGrid.Columns>
を指定しなかった場合は以下のような出力になります(カラムの名前に注目してください)
UserEnitity
のプロパティ名が DataGrid のカラム名になっていることが分かります。
SelectionChanged
以下のコードは DataGrid の 1 行をクリックしたときに値を取得する処理を実装しています。
<!-- 省略 -->
<DataGrid
x:Name="dataGridForUserData"
Grid.Row="1"
d:ItemsSource="{d:SampleData ItemCount=5}"
AutoGenerateColumns="False"
Cursor="Hand"
GridLinesVisibility="All"
IsReadOnly="True"
ItemsSource="{Binding UserEntityData}"
VerticalScrollBarVisibility="Auto">
<Behaviors:Interaction.Triggers>
<Behaviors:EventTrigger EventName="SelectionChanged">
<Behaviors:InvokeCommandAction Command="{Binding ExecuteRowSelectedCommand}" CommandParameter="{Binding ElementName=dataGridForUserData, Path=SelectedItem}" />
</Behaviors:EventTrigger>
</Behaviors:Interaction.Triggers>
<!-- 省略 -->
Microsoft.Xaml.Behaviors.Wpf
というパッケージを使用して特定のイベントをトリガーしています。
どのようなイベントが設定できる調べるには、公式のDataGrid クラスを参照しても良いですが、サッと確認するときは ビジュアルデザイナー内の DataGrid コントロールを選択してプロパティからイベントを確認することが出来ます(イナズママークを選択)。
SelectionChanged
イベントで実行される処理の内容は以下のようになっています。
private void RowSelectedCommand(object o)
{
var entity = o as UserEnitity;
if (entity != null)
{
var displayValue = $"ユーザーID: {entity.Id}\n" +
$"名前: {entity.Name}\n" +
$"年齢: {entity.Age}\n" +
$"誕生日: {entity.BrithDay}\n" +
$"性別: {entity.Sex}\n" +
$"メールアドレス: {entity.Email}\n" +
$"電話番号: {entity.Tell}";
MessageBox.Show($"{displayValue}",
"ユーザー情報",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
}
選択された行のプロパティを出力しているだけの実装となっていますが、選択された際に編集用画面を開いて選択された行の値を変更するフォームを実装するなど応用することも出来ます。
CSV から取得する
CSV からデータを取得する処理です。
注意点としては、.NET6
では SJIS エンコードを使用する際にEncoding.RegisterProvider
を呼ぶ必要があります。
using DataGrid_Sample.Enitities;
using DataGrid_Sample.Repositories;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
namespace DataGrid_Sample.Csv
{
public sealed class CSVClient : IDataGridRepository
{
/// <summary>
/// 現在処理中の行が1行目か判定する
/// </summary>
private bool _isFirstLine = true;
/// <summary>
/// アプリケーションが実行されているディレクトリを取得し、指定した文字列を結合させる
/// </summary>
private string _currentDomain = AppDomain.CurrentDomain.BaseDirectory;
public ObservableCollection<UserEnitity> Get()
{
var entity = new ObservableCollection<UserEnitity>();
int indexCount = 0;
try
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
using (var reader = new StreamReader(_currentDomain + "dummyData.csv", Encoding.GetEncoding("Shift_JIS")))
{
// 1.whileで1行ずつ読み込む
// 2.最初の行はスキップする
// 3.split(",") でカンマを基準に各要素を配列に変換する
// 4.UserEntityに必要な要素だけインデックスを指定して取り出す
while (reader.Peek() >= 0)
{
var line = reader.ReadLine();
if (_isFirstLine)
{
_isFirstLine = false;
continue;
}
var strArray = line.Split(",");
indexCount++;
entity.Add(new UserEnitity(
indexCount,
strArray[0],
strArray[3],
strArray[4],
strArray[5],
strArray[7],
strArray[8]
)
);
}
return entity;
}
}catch (Exception)
{
throw;
}
}
}
}
まとめ
今回は MVVM で DataGrid の実装について紹介しました。
今回使用したコードはGitHub リポジトリで公開しています。