![見出し画像](https://assets.st-note.com/production/uploads/images/35914746/rectangle_large_type_2_fa62ad5260d31bea35a616a624dc2715.png?width=1200)
DataGridView (02)
#VisualStudio2019 #NET #DataTable #DataGridView
目的
VisualStudio 2019 上の C#言語と .Net Framework を用いて、データを定義したり、操作したり、画面上に表示したりというようなことについて、さまざまな角度から説明します。
基本的に、「このとおりにやれば、確実に、プログラムは動く」というものを目指しています。
今回の目的は、「自分がいま、どこのセルに居るのかがわかるようにすること」と、「そのセルの内容を表示すること」です。この場合の「内容」は、データグリッドビューの内容ではなく、リンクしてあるデータそのものの内容です。
準備
前回の記事で、データを作成し、表示させることができています。
このプログラムを多少改変し、アプリケーション全体について、データについて、データグリッドビューについて、の3つの部分に分けて書き直したものが、次のプログラムです。
ソースコードは、次のとおりです。
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); // 画面に表示
}
}
}
このプログラムに、本日の内容を追加していきます。
ラベルを表示させる
まず、「現在地を表示させるためのラベル」が必要です。
Label L1; // 表示用ラベル
このラベルを、ア、実際に作成し、イ、アプリ上で表示させ、ウ、位置や大きさやフォント等を設定する、という作業が必要です。また、ラベルに表示させる文字は、最初は「最初のL1」という語句にしておきます。
実行結果は、次のとおりです。
イベントを取得する
次に、「現在、どこにいるのか」を調べます。そのためには、「セルを移動した」とか「セルをクリックした」という情報を取得しなければなりません。このような情報のことを、「イベント」と呼んでいます。
たとえば、データグリッドビューをクリックしたときに、何らかのプログラムを起動させたければ、
dgv.Click
と記述します。これは、ラベルの場合も同じで、L1 というラベルをクリックしたときに、プログラムを起動させたければ、
L1.Click
と記述します。しかし、これだけでは、どんなことをさせたいのか、が分かりません。そこで「関数」を使って、なにをさせたいのかを記述します。
一番単純なのは、「セルをクリックしました」というメッセージを出すことで、次のように記述します。
private void dgv_Click() {
Message.Show("セルをクリックしました");
}
そこで、上に述べたように、「dgv のセルをクリックしたら、この関数を呼び出して、メッセージを表示する」という動きをつくりたいのですが、このときのプログラムの書き方が、けっこう面倒です。
まず、呼び出し側は、厳密には次のように書かなければなりません
dgv.Click += new System.EventHandler(dgv_click);
dgv に関する記述の近くにこれを書いておきます。
次に、dgv_click 関数ですが、実際には、次の黄色い部分
に記述します。具体的に次のとおりです。
ところが、このままではエラーになります。具体的には、次のようなエラーが表示されます。
「デリケート」とか「オーバーロード」という言葉を調べ始めると、どんどん迷路に入り込んでしまいます。これは、次のように書き直すとエラーはでなくなります。
ここまでの動きを動画にすると次のようになります。
また、ここまでの全体のソースコードは次のとおりです。
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); // ラベルを表示させる
}
}
}
イベントの取得は、手続きがとても面倒
ただし、上のような作法は、はっきり言って面倒です。
dgv.Click += new System.EventHandler(dgv_click);
この部分の書き方は、ラベルやボタンなど種類によって変わってきます。具体的には「 EventHandler 」の部分が別の用語に変わります。さらに、
private void dgv_click(object s, EventArgs e)
こちらの部分も変更しなければなりません。しかも、カッコのなかは、「s」ともうひとつの「e」の2つで決まっています。減らすことも増やすこともできません。これ以外の引数を設定しようとすると、エラーになります。
たとえば、さきほどの関数で、「現在の座標は (10, 20) です」のような表示をしたいと考えます。
この関数そのものは、エラーにはなりません。ところが、呼び出す側では、次のようにエラーが発生します。
問題なのは、これを修正不可能だということです。標準的な方法では、「int x, int y」は使えません。削除しなければ、正常に動作しません。つまり、もしも関数の中でどうしても x と y を使いたいのであれば、
などのように、x と y を、大域的変数として定義するしかない、ということになります。
イベント処理を定義するたびに、大域的変数がどんどん増えていくというのでは、面倒くさくて仕方ありません。ミスの原因になるだろうと思われます。これは、あまりよい方法ではありません。
イベントの取得を、楽にする方法
そこで、裏技を使うことにします。もちろん、この裏技は、先輩諸氏に教えていただいたものです(下の方に出典を示します)。具体的には、次のようにします。
データグリッドビューをクリックしたときに、関数を呼び出す部分
dgv.Click += (s,e) => dgv_click();
関数本体
private void dgv_click()
{
MessageBox.Show("セルをクリックしました");
}
これだけです。引数はつけてもいいし、つけなくてもいいし、任意につけることができます。
クリックしたときの現在の位置を取得する
さて、データグリッドビューをクリックしたときに、クリックしたセルの色が変わります。このセルの座標を「 (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() + ")";
}
この場合のアプリケーションの動作は次のようになります。
ごらんのとおり、クリックすると、ちゃんと表示されます。
ところが、矢印キーで移動させるとうまく表示されません。
カーソルキーの移動のときも座標を表示させる
このような動作になった原因は、次のところにあります。
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)
表示しているモノではなく、もともとのデータの内容を確認する
以上のようにして、「現在、自分が居る場所」の確認ができたら、次は「自分が居る場所のデータ」の確認作業に移ります。この場合、注意が必要なのは、dgv は単なる表示に過ぎないというとです。もともとのデータは dt に入っているのですから、dt の内容を確認することが重要です。特に、dgv の見た目でデータが変更されたときに、もともとの dt のデータも自動的に更新されるのか確認しなければなりません。
最初に表示用のラベルを作っておきます。
ラベルの位置と、色を変えておきます。この状態でプログラムを動かすと、次のようになります。
次に、この L2 にデータの内容を表示させます。データは「dt」に入っていて、座標は (x,y) で得られますので、次のようにして L2 に内容を表示させます。
Label2.Text = "中身 " + dt.Rows[y][x]
ただし、ごらんのとおり、dt から内容を取得する方法がわかりにくくなっています。「x, y」という順番ではなく、「 Rows[y][x] 」という順番になっているので注意が必要です。プログラムは次のようになります。
このプログラムには問題点がある
もともとのデータ「dt」は、10 行ありました。つまり 1行から10行までデータ(を格納する場所)が存在します。
ところが、11行目は、データグリッドビューには存在しますが、「dt」には存在しません。したがって、カーソルを 11行目に移動させようとするとエラーが発生します。
このエラーをどのように回避するかについては、次の記事で述べます。
ウラワザの出典
以前、teratail で私が質問した際に、「mmaeda」さんからヒントをいただきました。具体的に次のとおりです。
この記事のなかで、「mmaeda」さんの以下の投稿により、教えていただきました。「anonymous」と「クロージャー」というキーワードが出てきます。これらについて調べてみると、おもしろいことがたくさんありますが、それはまた別項で述べる予定です。
ソースコード
この記事の最終的なソースコードは次のとおりです。
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); // ラベルを表示させる
}
}
}
参考(リンク集)
他のページへのリンクをすべて書いていると、それぞれのページでの更新がとても大変になるので、一括してリンクページで管理します。
⇒ DataGridView に関するページのリンク集