はじめに
この記事では、DataGridを階層化アーキテクチャで使う方法をテーマにしたいと思います。
以前紹介した「【C# WPF】DataGridの使い方の基礎」を元に、階層化アーキテクチャに変更してみます。
プロジェクトを作成して階層化
まず、プロジェクト「Domain」を追加します。
- ソリューションエクスプローラで「ソリューション」を選択して、右クリック・メニューから「追加」→「新しいプロジェクト」と進む。
- 「新しいプロジェクトを追加」というパネルが開くので、右枠の中で C#の「クラス ライブラリ」を選んで「次へ」をクリック。
- 「新しいプロジェクトを構成します」というパネルが開くので、「プロジェクト名」を「Domain」として、「次へ」をクリック。
次に参照を設定します。
- ソリューションエクスプローラでプロジェクト「WpfApp1」の「依存関係」を右クリック→「プロジェクト参照の追加」を選びます。
- 「参照マネージャー」が開くので、「Domain」の左にあるチェックボックスをオンにして「OK」をクリック。
「依存関係」の右下に警告アイコンが出たり、「NU1201:プロジェクト Domain は net7.0-windows7.0 (.NETCoreApp,Version=v7.0) と互換性がありません。」といったエラーが出た場合は、「WpfApp1」を右クリックして「プロパティ」を選ぶ、タブ画面が開くので「ターゲット フレームワーク」をそろえます。今回は「.NET 8.0」です。
しばらくするとエラーが消えるはず。
Modelを編集
Domainプロジェクトにコードを移します。
「MainWindow.xaml.cs」から「Model.cs」へコードを移します。
- Domainプロジェクトの中にある「Class1.cs」を「Model.cs」に名称変更します。「ソリューション・エクスプローラ」で選択して「F2」を押せばできます。
- 「Model.cs」を開いて、「ValueObject0」というクラスを定義します。そのクラスの中に、「MainWindow.xaml.cs」から次のコードをカット&ペーストします。
- 列の項目をプロパティとして定義した、行を表すクラス「DataGrid1Row」
- 上の行クラスを型にして、定義したコレクション「DataGrid1Rows」
- 同様に、3つあるボタンのイベントを処理してた部分を、それぞれメソッドとして定義します。名前は「AddRow」「ReplaceRow」「RemoveRow」としましょう。なお、RefreshメソッドとMessageBoxの処理は、移さず「MainWindow.xaml.cs」の方に残します。
以上を行うと「Model.cs」次のようになります。
namespace Domain
{
public class ValueObject0
{
public ValueObject0() {
DataGrid1Rows = new ObservableCollection<DataGrid1Row> { };
}
//プロパティの定義
public ObservableCollection<DataGrid1Row> DataGrid1Rows { get; set; }
public class DataGrid1Row
{
public string Item1 { get; set; } = "";
}
//追加の処理
private int testCounter = 0;
public void AddRow()
{
DataGrid1Rows.Add(new DataGrid1Row { Item1 = $"追加データ{testCounter}" });
testCounter++;
}
//変更の処理
public void ReplaceRow()
{
foreach (DataGrid1Row row in DataGrid1Rows)
{
row.Item1 = row.Item1.Replace("追加", "変更済");
}
}
//削除の処理
public void RemoveRow()
{
DataGrid1Rows.RemoveAt(0);
}
}
}
コレクションの型は、自動で更新通知が発行されるので「ObservableCollection」がオススメ。
(高度な連携をする場合には手動通知が必須になるケースもあります)
ViewModelを追加
データ バインディングで受け渡す際に橋渡しをするクラスを定義します。
「WpfApp1」の中にファイル「ViewModel.cs」を作成しましょう。その中で「ValueObject0」を生成し、生成したオブジェクトを返すプロパティを用意します。
using Domain;
namespace WpfApp1
{
public class ViewModel
{
private readonly ValueObject0 _valueObject0 = new();
public ValueObject0 ValueObject0 { get { return _valueObject0; } }
}
}
このクラスをプログラム開始時に生成するため「App.xaml.cs」にコンストラクタを追記します。
次のようになります。
public partial class App : Application
{
public static ViewModel? ViewModel { get; set; }
App()
{
ViewModel = new();
}
}
画面まわりを編集
MainWindow.xaml に「ItemsSource=」を追加し、「ValueObject0.DataGrid1Rows」をバインディングします。
Item1の方は、元からバインディングされています。このバインディングも必要ですのでこのまま残します。
<DataGrid x:Name="DataGrid1" ItemsSource="{Binding ValueObject0.DataGrid1Rows}"
AutoGenerateColumns="False" CanUserAddRows="False" Margin="10,10,10,10">
~略~
<DataGridTextColumn Header="アイテム1" Binding="{Binding Item1}" />
MainWindow.xaml.csを編集します。
- 「DataGrid1.ItemsSource = DataGrid1Rows;」は不要になるので削除。
- 「DataContext = App.ViewModel;」を追加。
- ボタンのイベントは、Modelのメソッドを呼ぶように変更。
ModelでMessageBoxを出そうとすると、Windowへの参照が必要になり好ましくないので、今回は「~.xaml.cs」の方で行数を数えて対応することにします。
DataGrid1.Items.Refreshも同様に残します。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = App.ViewModel; //Xamlへデータをつなぐために必要
}
//行追加の処理
private void Button1_Click(object sender, RoutedEventArgs e)
{
App.ViewModel!.ValueObject0.AddRow();
}
//行変更の処理
private void Button2_Click(object sender, RoutedEventArgs e)
{
App.ViewModel!.ValueObject0.ReplaceRow();
DataGrid1.Items.Refresh();
}
//行削除の処理
if (DataGrid1.Items.Count > 0)
{
App.ViewModel!.ValueObject0.RemoveRow();
}
else
{
MessageBox.Show("削除できません。行がありません。");
}
}
まとめ
今回は、DataGridを階層化アーキテクチャで使う方法をまとめました。
ポイントは次のようになります。
- データを格納するクラス
- 列の項目をプロパティとして行のクラスを定義する(列をまとめて行を定義)
- 行のクラスを型にして、コレクションを定義する(行の集合、つまり行列全体を定義)
- コレクションの型は、自動で更新通知が発行されるので「ObservableCollection」がオススメ
(高度な連携をする場合には手動通知が必須になるケースもあります)
- バインディング
- DataGridタグのItemsSourceにコレクションをバインディングする。
- 列の項目に列の項目のプロパティをバインディングする。
コメント