見出し画像

DataGridView (02)

#VisualStudio2019 #NET #DataTable #DataGridView


目的

画像18

VisualStudio 2019 上の C#言語と .Net Framework を用いて、データを定義したり、操作したり、画面上に表示したりというようなことについて、さまざまな角度から説明します。

基本的に、「このとおりにやれば、確実に、プログラムは動く」というものを目指しています。

今回の目的は、「自分がいま、どこのセルに居るのかがわかるようにすること」と、「そのセルの内容を表示すること」です。この場合の「内容」は、データグリッドビューの内容ではなく、リンクしてあるデータそのものの内容です。


準備

画像19

前回の記事で、データを作成し、表示させることができています。

⇒ DataGridView (01)

このプログラムを多少改変し、アプリケーション全体について、データについて、データグリッドビューについて、の3つの部分に分けて書き直したものが、次のプログラムです。

画像1

ソースコードは、次のとおりです。

using System.Data;
using System.Drawing;
using System.Windows.Forms;

namespace DGV02
{
   // あるセルをクリックする
   // あるセルにカーソルで移動する
   // ⇒セルの位置を取得し、セルの内容を確認する。

   public partial class Form1 : Form
   {

       DataTable dt; //     元のデータ
       DataGridView dgv; // 実際に表示するもの

       public Form1()
       {
           InitializeComponent();

           // アプリケーション全体
           this.Text = "データグリッドビュー DGV02"; // アプリケーションのタイトル
           this.Size = new Size(1400, 850); //          アプリケーション全体の大きさ
           StartPosition = FormStartPosition.Manual; // 起動時の位置を設定する
           DesktopLocation = new Point(20, 20); //      起動時の位置

           // データ作成
           dt = new DataTable();
           dt.Columns.Add("番号");
           dt.Columns.Add("氏名");
           dt.Columns.Add("点数");
           for (int i = 0; i < 10; i++)
           {
               DataRow dr = dt.NewRow();
               dt.Rows.Add(dr);
           }

           // データグリッドビュー
           dgv = new DataGridView();
           dgv.Size = new Size(1340, 740); //   表の大きさ
           dgv.Location = new Point(20, 50); // 表の位置

           dgv.DataSource = dt; //    データとデータグリッドビューを接続
           this.Controls.Add(dgv); // 画面に表示
       }
   }
}

このプログラムに、本日の内容を追加していきます。


ラベルを表示させる

画像20

まず、「現在地を表示させるためのラベル」が必要です。

画像2

Label L1; // 表示用ラベル

このラベルを、ア、実際に作成し、イ、アプリ上で表示させ、ウ、位置や大きさやフォント等を設定する、という作業が必要です。また、ラベルに表示させる文字は、最初は「最初のL1」という語句にしておきます。

画像3

実行結果は、次のとおりです。

画像4


イベントを取得する

画像21

次に、「現在、どこにいるのか」を調べます。そのためには、「セルを移動した」とか「セルをクリックした」という情報を取得しなければなりません。このような情報のことを、「イベント」と呼んでいます。

たとえば、データグリッドビューをクリックしたときに、何らかのプログラムを起動させたければ、

dgv.Click

と記述します。これは、ラベルの場合も同じで、L1 というラベルをクリックしたときに、プログラムを起動させたければ、

L1.Click

と記述します。しかし、これだけでは、どんなことをさせたいのか、が分かりません。そこで「関数」を使って、なにをさせたいのかを記述します。

一番単純なのは、「セルをクリックしました」というメッセージを出すことで、次のように記述します。

private void dgv_Click() {
     Message.Show("セルをクリックしました");
}

そこで、上に述べたように、「dgv のセルをクリックしたら、この関数を呼び出して、メッセージを表示する」という動きをつくりたいのですが、このときのプログラムの書き方が、けっこう面倒です。

まず、呼び出し側は、厳密には次のように書かなければなりません

dgv.Click += new System.EventHandler(dgv_click);

dgv に関する記述の近くにこれを書いておきます。

画像5

次に、dgv_click 関数ですが、実際には、次の黄色い部分

画像6

に記述します。具体的に次のとおりです。

画像7

ところが、このままではエラーになります。具体的には、次のようなエラーが表示されます。

画像8

「デリケート」とか「オーバーロード」という言葉を調べ始めると、どんどん迷路に入り込んでしまいます。これは、次のように書き直すとエラーはでなくなります。

画像9

ここまでの動きを動画にすると次のようになります。

また、ここまでの全体のソースコードは次のとおりです。

using System;
using System.Data;
using System.Drawing;
using System.Runtime.Remoting.Channels;
using System.Windows.Forms;

namespace DGV02
{
   // あるセルをクリックする
   // あるセルにカーソルで移動する
   // ⇒セルの位置を取得し、セルの内容を確認する。
   public partial class Form1 : Form
   {
       DataTable dt; //     元のデータ
       DataGridView dgv; // 実際に表示するもの
       Label L1; // 表示用ラベル
       
       private void dgv_click(object s, EventArgs e)
       {
           MessageBox.Show("セルをクリックしました");
       }
       
       public Form1()
       {
           InitializeComponent();
       
           // アプリケーション全体
           this.Text = "データグリッドビュー DGV02"; // アプリケーションのタイトル
           this.Size = new Size(1400, 850); //          アプリケーション全体の大きさ
           StartPosition = FormStartPosition.Manual; // 起動時の位置を設定する
           DesktopLocation = new Point(20, 20); //      起動時の位置
       
           // データ作成
           dt = new DataTable();
           dt.Columns.Add("番号");
           dt.Columns.Add("氏名");
           dt.Columns.Add("点数");
           for (int i = 0; i < 10; i++)
           {
               DataRow dr = dt.NewRow();
               dt.Rows.Add(dr);
           }
       
           // データグリッドビュー
           dgv = new DataGridView();
           dgv.Size = new Size(1340, 740);   // 表の大きさ
           dgv.Location = new Point(20, 50); // 表の位置
           dgv.DataSource = dt;    // データとデータグリッドビューを接続
           this.Controls.Add(dgv); // 画面に表示
           dgv.Click += new System.EventHandler(dgv_click);
       
           // ラベルの設定
           L1 = new Label();                   // ラベルをつくる
           L1.Location = new Point(20, 5);     // ラベルの位置
           L1.Size = new Size(200, 40);        // ラベルのサイズ
           L1.BackColor = Color.LightPink;     // ラベルの背景色
           L1.Text = "最初のL1";               // ラベルに表示する文字
           L1.Font = new Font("メイリオ", 18); // ラベルの文字種や大きさ
           L1.TextAlign = ContentAlignment.MiddleCenter; // 文字をラベルの中心に表示する
           this.Controls.Add(L1);              // ラベルを表示させる
       }
   }
}


イベントの取得は、手続きがとても面倒

画像22

ただし、上のような作法は、はっきり言って面倒です。

dgv.Click += new System.EventHandler(dgv_click);

この部分の書き方は、ラベルやボタンなど種類によって変わってきます。具体的には「 EventHandler 」の部分が別の用語に変わります。さらに、

private void dgv_click(object s, EventArgs e)

こちらの部分も変更しなければなりません。しかも、カッコのなかは、「s」ともうひとつの「e」の2つで決まっています。減らすことも増やすこともできません。これ以外の引数を設定しようとすると、エラーになります。

たとえば、さきほどの関数で、「現在の座標は (10, 20) です」のような表示をしたいと考えます。

画像10

この関数そのものは、エラーにはなりません。ところが、呼び出す側では、次のようにエラーが発生します。

画像11

問題なのは、これを修正不可能だということです。標準的な方法では、「int x, int y」は使えません。削除しなければ、正常に動作しません。つまり、もしも関数の中でどうしても x と y を使いたいのであれば、

画像12

などのように、x と y を、大域的変数として定義するしかない、ということになります。

イベント処理を定義するたびに、大域的変数がどんどん増えていくというのでは、面倒くさくて仕方ありません。ミスの原因になるだろうと思われます。これは、あまりよい方法ではありません。


イベントの取得を、楽にする方法

画像23

そこで、裏技を使うことにします。もちろん、この裏技は、先輩諸氏に教えていただいたものです(下の方に出典を示します)。具体的には、次のようにします。

データグリッドビューをクリックしたときに、関数を呼び出す部分

dgv.Click += (s,e) => dgv_click();

関数本体

private void dgv_click()
{
  MessageBox.Show("セルをクリックしました");
}

これだけです。引数はつけてもいいし、つけなくてもいいし、任意につけることができます。


クリックしたときの現在の位置を取得する

画像24

さて、データグリッドビューをクリックしたときに、クリックしたセルの色が変わります。このセルの座標を「 (x,y) 」という形で表そうと考えます。このとき、x と y の値は、次のような関数で取得できます。

x = dgv.CurrentCellAddress.X;
y = dgv.CurrentCellAddress.Y;

この x と y をラベルに表示させます。

ラベルの名前は「L1」ですから、具体的には次のようにします。

L1.Text = "クリック (" + x.ToString() + "," + y.ToString() + ")";

以上をまとめて、「クリックしたときに、ラベルに座標を表示させる関数」は、次のようになります。

private void dgv_click()
{
   x = dgv.CurrentCellAddress.X;
   y = dgv.CurrentCellAddress.Y;
   L1.Text = "クリック (" + x.ToString() + "," + y.ToString() + ")";
}

この場合のアプリケーションの動作は次のようになります。

ごらんのとおり、クリックすると、ちゃんと表示されます。

ところが、矢印キーで移動させるとうまく表示されません。


カーソルキーの移動のときも座標を表示させる

画像25

このような動作になった原因は、次のところにあります。

dgv.Click += (s,e) => dgv_click();

この関数では、クリックには反応しますが、カーソルの移動には反応しません。そこで、データグリッドビューをクリックするだけでなく、移動したときにも関数を呼び出すようにします。具体的には、

dgv.CellEnter += (s,e) => dgv_click();

と変更すると、うまく動きます。このように変更した場合の動作について、次の動画で確認できます。

なお、今回は、Click を CellEnter に変更しただけで特に問題なく動きましたが、正式な書き方にすると、この2つは別の種類のものになります。まず、呼び出す側の記述には、次ような違いがあります。

dgv.CellEnter += new DataGridViewCellEventHandler(dgv_CellEnter);

dgv.Click += new EventHandler(dgv_Click);

また、呼び出される関数の記述にも、次のような違いがあります。

private void dgv_CellEnter(object sender, DataGridViewCellEventArgs e)

private void dgv_Click(object sender, EventArgs e)


表示しているモノではなく、もともとのデータの内容を確認する

画像26

以上のようにして、「現在、自分が居る場所」の確認ができたら、次は「自分が居る場所のデータ」の確認作業に移ります。この場合、注意が必要なのは、dgv は単なる表示に過ぎないというとです。もともとのデータは dt に入っているのですから、dt の内容を確認することが重要です。特に、dgv の見た目でデータが変更されたときに、もともとの dt のデータも自動的に更新されるのか確認しなければなりません。

最初に表示用のラベルを作っておきます。

画像14

ラベルの位置と、色を変えておきます。この状態でプログラムを動かすと、次のようになります。

画像15

次に、この L2 にデータの内容を表示させます。データは「dt」に入っていて、座標は (x,y) で得られますので、次のようにして L2 に内容を表示させます。

Label2.Text = "中身 " + dt.Rows[y][x]

ただし、ごらんのとおり、dt から内容を取得する方法がわかりにくくなっています。「x, y」という順番ではなく、「 Rows[y][x] 」という順番になっているので注意が必要です。プログラムは次のようになります。

画像16


このプログラムには問題点がある

画像27

もともとのデータ「dt」は、10 行ありました。つまり 1行から10行までデータ(を格納する場所)が存在します。

ところが、11行目は、データグリッドビューには存在しますが、「dt」には存在しません。したがって、カーソルを 11行目に移動させようとするとエラーが発生します。

画像17

このエラーをどのように回避するかについては、次の記事で述べます。


ウラワザの出典

画像28

以前、teratail で私が質問した際に、「mmaeda」さんからヒントをいただきました。具体的に次のとおりです。

この記事のなかで、「mmaeda」さんの以下の投稿により、教えていただきました。「anonymous」と「クロージャー」というキーワードが出てきます。これらについて調べてみると、おもしろいことがたくさんありますが、それはまた別項で述べる予定です。

画像13


ソースコード

画像29

この記事の最終的なソースコードは次のとおりです。

using System.Data;
using System.Drawing;
using System.Windows.Forms;

namespace DGV02
{
   // あるセルをクリックする
   // あるセルにカーソルで移動する
   // ⇒セルの位置を取得し、セルの内容を確認する。

   public partial class Form1 : Form
   {
       DataTable dt; //     元のデータ
       DataGridView dgv; // 実際に表示するもの
       Label L1,L2; // 表示用ラベル

       private void dgv_Enter()
       {
           int x = dgv.CurrentCellAddress.X;
           int y = dgv.CurrentCellAddress.Y;
           L1.Text = "座標 (" + x.ToString() + "," + y.ToString() + ")";
           L2.Text = "内容 (" + dt.Rows[y][x] + ")";
       }

       public Form1()
       {
           InitializeComponent();

           // アプリケーション全体
           this.Text = "データグリッドビュー DGV02";    // アプリケーションのタイトル
           this.Size = new Size(1400, 850);          // アプリケーション全体の大きさ
           StartPosition = FormStartPosition.Manual; // 起動時の位置を設定する
           DesktopLocation = new Point(20, 20);      // 起動時の位置

           // データ作成
           dt = new DataTable();
           dt.Columns.Add("番号");
           dt.Columns.Add("氏名");
           dt.Columns.Add("点数");
           for (int i = 0; i < 10; i++)
           {
               DataRow dr = dt.NewRow();
               dt.Rows.Add(dr);
           }

           // データグリッドビュー
           dgv = new DataGridView();
           dgv.Size = new Size(1340, 740);   // 表の大きさ
           dgv.Location = new Point(20, 50); // 表の位置
           dgv.DataSource = dt;    // データとデータグリッドビューを接続
           this.Controls.Add(dgv); // 画面に表示
           dgv.CellEnter += (s,e) => dgv_Enter();

           // 「現在の自分の位置を示すラベル」の設定
           L1 = new Label();                   // ラベルをつくる
           L1.Location = new Point(20, 5);     // ラベルの位置
           L1.Size = new Size(200, 40);        // ラベルのサイズ
           L1.BackColor = Color.LightPink;     // ラベルの背景色
           L1.Text = "座標 L1";                 // ラベルに表示する文字
           L1.Font = new Font("メイリオ", 18);   // ラベルの文字種や大きさ
           L1.TextAlign = ContentAlignment.MiddleCenter; // 文字をラベルの中心に表示する
           this.Controls.Add(L1);               // ラベルを表示させる

           // 「現在地のセルの内容を示すラベル」の設定
           L2 = new Label();                   // ラベルをつくる
           L2.Location = new Point(240, 5);    // ラベルの位置
           L2.Size = new Size(200, 40);        // ラベルのサイズ
           L2.BackColor = Color.LightCyan;     // ラベルの背景色
           L2.Text = "内容 L2";                 // ラベルに表示する文字
           L2.Font = new Font("メイリオ", 18);   // ラベルの文字種や大きさ
           L2.TextAlign = ContentAlignment.MiddleCenter; // 文字をラベルの中心に表示する
           this.Controls.Add(L2);               // ラベルを表示させる
       }
   }
}


参考(リンク集)

画像30

他のページへのリンクをすべて書いていると、それぞれのページでの更新がとても大変になるので、一括してリンクページで管理します。
⇒ DataGridView に関するページのリンク集





この記事が気に入ったらサポートをしてみませんか?