DataGridViewで作る簡易レジアプリ|プロジェクト作成から学ぶC#実践課題
WinFormsで何か実践的な題材を探しているなら、DataGridView を使った簡易レジアプリ(POS風アプリ)がおすすめです。商品を追加するたびに「商品名・単価・数量・小計」を一覧表示する場面は、まさに DataGridView の得意分野。今回はプロジェクトの作成手順から、初学者が段階的にレベルアップできる教材として紹介します。
完成イメージ
| 商品名 | 単価 | 数量 | 小計 |
|---|---|---|---|
| コーヒー | 150円 | 2 | 300円 |
| パン | 180円 | 1 | 180円 |
| おにぎり | 130円 | 3 | 390円 |
表の「円」は完成イメージ用です。以下のサンプルでは金額を整数で表示します。この表を DataGridView で表示し、その下に合計金額・預かり金・お釣りを表示すれば、レジアプリらしい画面が比較的簡単に完成します。
ステップ0:プロジェクトを作成する
まずはVisual Studioで新規プロジェクトを作成します。初学者がつまずきやすいポイントなので、丁寧に確認しておきましょう。
- Visual Studioを起動し、「新しいプロジェクトの作成」を選択
- テンプレート一覧から 「Windows Forms アプリ (.NET)」 を選択(似た名前で「Windows Forms アプリ (.NET Framework)」もあるため、間違えないよう注意。新規学習には
.NET版を推奨) - プロジェクト名を入力(例:
SimpleRegisterApp) - ソリューション名は自動的にプロジェクト名と同じになる(そのままでOK。将来複数プロジェクトを1つのソリューションにまとめたい場合のみ分ける)
- 保存場所を指定して「作成」をクリック
- フレームワークは最新のLTS版(例:.NET 10)を選択
ここで「ソリューション」と「プロジェクト」の違い(ソリューション=複数プロジェクトをまとめる箱、プロジェクト=実際にビルドされる単位)を軽く説明しておくと、後々の理解がスムーズになります。
ステップ1:DataGridViewに列を登録する
フォームに DataGridView を配置したら、まず列(カラム)を定義します。コントロールの名前(Name)は dataGridView1 とします。列はデザイナーかコードのどちらか一方で設定してください。両方で追加すると列が重複します。コードで設定する場合は、Form1 のコンストラクター内の InitializeComponent(); の後に記述し、初期化時に1回だけ実行します。
サンプルコードと解説を見る
デザイナーで設定する場合
- フォーム上の DataGridView を選択
- 右上のタスクメニューから「列の編集」を選択
- 「追加」ボタンで列を4つ作成し、Name(内部の名前)を
ProductName/Price/Quantity/Subtotal、HeaderText(見出し)を「商品名」/「単価」/「数量」/「小計」に設定
コードで設定する場合
dataGridView1.Columns.Add("ProductName", "商品名");
dataGridView1.Columns.Add("Price", "単価");
dataGridView1.Columns.Add("Quantity", "数量");
dataGridView1.Columns.Add("Subtotal", "小計");
解説
Columns.Add(列名, 表示テキスト)の第1引数は内部的な識別名(英数字推奨)、第2引数が画面に表示されるヘッダー文字列です。この区別を最初に理解しておくと、後でコードから特定の列を参照する際(dataGridView1.Columns["Price"]など)につまずきません。- デザイナーとコード、どちらで設定しても結果は同じです。初学者にはまずデザイナーで視覚的に理解してもらい、その後コードでも同じことができると示すと納得感が生まれます。
- 列の順番は Add した順(またはデザイナーでの並び順)がそのまま表示順になります。
列の登録がある程度理解できたら、続けて行を追加していきましょう。
ステップ2:DataGridViewに直接行を追加する
列が定義できたら、一番シンプルな方法でデータを表示してみます。次のコードは、コンストラクター内の列を準備した処理の後に記述します。ステップ2と3では DataSource を設定せず、行を直接追加します。
dataGridView1.Rows.Add("コーヒー", 150, 2, 300);
dataGridView1.Rows.Add("パン", 180, 1, 180);
dataGridView1.Rows.Add("おにぎり", 130, 3, 390);
この段階では「画面に表示する」ことだけに集中でき、DataGridView の基本的な使い方(列の追加、行の追加)を学ぶのに向いています。
ステップ3:Productクラス+List<Product>へ発展
慣れてきたら、商品データを構造化しましょう。Product クラスを作り、List<Product> で管理することで、「データ」と「表示」を分離する考え方を学べます。ステップ1の列はそのまま使い、ステップ2の行追加コードは、以下の使用例に置き換えてください。
サンプルコードと解説を見る
// Product.csに記述(Form1と同じ名前空間に置く)
class Product
{
public string Name { get; set; } = "";
public int Price { get; set; }
public int Quantity { get; set; }
public int Subtotal => Price * Quantity;
}
// ここからはForm1のコンストラクター内に記述
// InitializeComponent()と列の準備の後で実行する
List<Product> products = new List<Product>
{
new Product { Name = "コーヒー", Price = 150, Quantity = 2 },
new Product { Name = "パン", Price = 180, Quantity = 1 },
new Product { Name = "おにぎり", Price = 130, Quantity = 3 }
};
// 再表示する場合にも行が重複しないよう、表示中の行を消す
dataGridView1.Rows.Clear();
foreach (var p in products)
{
dataGridView1.Rows.Add(p.Name, p.Price, p.Quantity, p.Subtotal);
}
解説
Subtotalを保存用のフィールドではなく=>を使った計算プロパティにしている点がポイントです。Productの単価や数量が変わっても、プロパティを参照するたびに小計を計算します。ただし、この例のRows.Addは値を画面にコピーするため、表示済みのセルは自動更新されません。変更後は行をクリアして、foreachでもう一度表示します。List<Product>にデータをまとめることで、「商品データの管理」と「画面への表示」が別々の処理になります。これはステップ2の書き方(Rows.Addを直接並べる)との大きな違いです。foreachでリストをループし、1件ずつDataGridViewに反映させる流れは、「データと画面表示を分けて考える」設計につながります。ステップ4では、手動で行を追加する処理をデータバインディングに置き換えます。Nameは空文字列で初期化し、Nullable参照型が有効な環境でも未初期化の警告が出ないようにしています。- 応用として、
productsをフォームのフィールドに移し、商品追加ボタンでAddした後に行をクリアして再表示する機能へ発展できます。Refresh()で画面を描き直すだけでは、リストの変更は反映されません。
ステップ4:DataTable・データバインディングへ
さらに発展させるなら、DataTable を使ったデータバインディングに挑戦します。DataGridView.DataSource に設定すると、バインドした DataTable の行追加・削除や値の変更が表示に反映されるため、「手動更新」から「自動反映」への発想の転換を学べます。
ステップ3からの切り替え:ステップ2・3のデータ作成と行追加の処理を、下のコードに置き換えます。ステップ1で手動追加した列を残したまま自動生成すると、列が重複する原因になります。今回はバインド前に既存の行と列をクリアし、AutoGenerateColumns = true で DataTable から列を自動生成します。ステップ1のコードによる列追加処理は削除してください。デザイナーで追加した列は、下の Columns.Clear() で実行時に取り除かれます。
Form1.cs の先頭に using System.Data; を追加します。以下はコンストラクター内の InitializeComponent(); の後に記述してください。後でボタンから同じ表を操作する場合は、table をフォームのフィールドとして保持します。
サンプルコードと解説を見る
// 以前の表示を解除し、手動追加した行と列を取り除く
dataGridView1.DataSource = null;
dataGridView1.Rows.Clear();
dataGridView1.Columns.Clear();
dataGridView1.AutoGenerateColumns = true;
// DataTableを準備
DataTable table = new DataTable();
table.Columns.Add("商品名", typeof(string));
table.Columns.Add("単価", typeof(int));
table.Columns.Add("数量", typeof(int));
table.Columns.Add("小計", typeof(int));
// 行を追加
table.Rows.Add("コーヒー", 150, 2, 300);
table.Rows.Add("パン", 180, 1, 180);
table.Rows.Add("おにぎり", 130, 3, 390);
// DataGridViewにバインド
dataGridView1.DataSource = table;
解説
DataTableはメモリ上に「表形式のデータ」を保持する入れ物です。列(Columns)と行(Rows)を持つ点はDataGridViewと似ていますが、あくまでデータそのものを管理するオブジェクトである点が違います。dataGridView1.DataSource = table;の1行で、DataTableの内容がそのまま画面に反映されます。行を追加・削除するときもtable.Rows.Add(...)やtable.Rows.RemoveAt(...)のようにデータ側を操作するだけでよく、DataGridViewの行を直接追加・削除する必要がありません。バインド後はdataGridView1.Rows.Add(...)を使わず、table.Rows.Add(...)を使います。- このサンプルの「小計」は固定値です。ステップ3の計算プロパティとは異なり、単価や数量を変更しても自動再計算されません。自動計算に発展させる場合は、小計列に
table.Columns["小計"]!.Expression = "[単価] * [数量]";を設定し、行追加では小計を渡さずtable.Rows.Add("コーヒー", 150, 2);のように先頭3列だけを指定します。!は直前に追加した列が存在することをコンパイラーに伝える記号です。 - これは「データと画面表示を分ける設計」の第一歩です。将来的にデータベースアクセスや、データバインディングを利用した画面づくりを学ぶ際の土台になります。
- ステップ3の
List<Product>との違いにも触れておくと理解が深まります。List<Product>は自分で定義したクラスの集合、DataTableは汎用的な表形式データという位置づけです。この例では、Productのリストを自動変換しているわけではなく、同じ商品をDataTableに作り直しています。DBやCSVから読み込んだデータも表形式で扱えますが、読み込み処理は別途必要です。
機能を段階的に増やす構成
初学者に達成感を持たせるには、以下の順番で機能を追加していくのがおすすめです。
- 商品追加 — ボタンで商品を1件ずつ追加
- 合計計算 — 小計の合計をリアルタイムで表示
- 選択行削除 — 間違えた行を選んで削除
- 会計処理 — 預かり金を入力してお釣りを計算
商品追加・削除の後に合計を再計算するなど、機能を少しずつ組み合わせることで、「今日はここまでできた」という達成感を積み重ねやすい構成になっています。
まとめ
プロジェクト作成、列の登録から始めることで、環境構築や画面設計でつまずきやすい初学者にも安心して取り組んでもらえます。DataGridView を使った簡易レジアプリは、表示だけでなく、クラス設計・データバインディング・イベント処理まで幅広く学べる題材です。職業訓練校の授業課題としても、C#の基礎から実践への橋渡しとして相性が良いのではないでしょうか。




ディスカッション
コメント一覧
まだ、コメントがありません