C# 入門講座
Visual Studio 2005 Visual Studio 2008 Visual Studio 2010 Visual Studio 2012 Visual Studio 2013 Visual Studio 2015 Visual Studio 2017 Visual Studio 2019

第4回 イベントを逃すな

この記事が対象とする製品・バージョン (バージョンの確認方法)

VS2019 Visual Studio 2019 対象です。
VS2017 Visual Studio 2017 対象です。
VS2015 Visual Studio 2015 対象です。
VS2013 Visual Studio 2013 対象です。
VS2012 Visual Studio 2012 対象ですが一部画面が異なる場合があります。
VS2010 Visual Studio 2010 対象ですが一部画面が異なる場合があります。
VS2008 Visual Studio 2008 対象ですが一部画面が異なる場合があります。
VS2005 Visual Studio 2005 対象ですが一部画面が異なる場合があります。
VS.NET 2003 Visual Studio 2003 対象外ですがほとんどの操作は同じなので参考になります。
VS.NET 2002 Visual Studio (2002) 対象外ですがほとんどの操作は同じなので参考になります。

入門講座の第4回では「イベント」について説明します。前回の「プロパティ」と今回の「イベント」の概要が分かれば、あともう少しで最低限必要な知識はそろいます。がんばって習得してください。

概要

・イベントが発生すると自動的にイベントハンドラーが実行される

・イベントハンドラーの作り方

・画像ファイルを表示させる

 

1.どこにプログラムするのか

 

たとえば、画像を表示するプログラムを作りたいと思ったとして、画像を表示させる方法はインターネット等で検索すればすぐ分かります。多分次のようなコード(プログラム)を発見することでしょう。


this.BackgroundImage = Image.FromFile(@"C:\Windows\Web\Wallpaper\Windows\img0.jpg");

■リスト1

this (読み方:this = ディス)はこの場合はフォームのことです。BackgroundImage(読み方:BackgroudImage = バックグラウンドイメージ)は 背景画像をあらわすプロパティです。

でも、このプログラムを一体どこに書けばよいのでしょうか?

イベント」をよく理解すればこういった問題で悩むことはあまりなくなります。これを読んでいる方はまだイベントについて何も知らないはずなので、丁寧に順を追って説明しましょう。

問題は「画像をいつ、どのタイミングで表示したいか?」ということです。

フォームが表示されたときはじめから画像を表示したいのか、それとも、ボタンが押されたときに表示したいのか、それともフォームをクリックしたら表示するのか…などなどです。

このようなタイミングのことを「イベント」といいます。

この例では「フォームが表示された」、「ボタンが押された」、「フォームをクリックした」などがイベントです。そしてイベントごとにプログラムを書く場所はある程度限定されます。

 

2.Clickイベント

 

では、具体的にプログラムしてみましょう。まずはボタンが押されたときに画像が表示されるようにしましょう。

「ボタンが押された」というイベントはClickイベント (読み方:Click = クリック)と呼びます。

ボタンがクリックされたときに何かを実行するというプログラムは入門講座でも既に登場しています。今回は「イベント」の視点で改めて眺めてみることにします。

 

まず、新しいWindowsフォームアプリケーションをを作成してください。

Windows フォーム アプリケーション

■画像1:新しいプロジェクトを作成した直後

 

そしてフォームにボタンを1つ貼り付けてください。

フォームにButtonを1つ貼り付けたところ

■画像2:ボタンを貼り付けた状態

 

このボタンがクリックされたらフォームに画像を表示することにします。

 

ボタンをダブルクリックしてください。そうすると、画面がコードエディターに切り替わります。

この操作はこれまでの入門講座でも何回かやっていますね。

 

ボタンをダブルクリックすることで次のコード(プログラム)が自動的に生成されますので、ボタンをダブルクリックするという手順がとても重要です。

private void Button1_Click(object sender, EventArgs e)
{

}

■リスト2:ボタンのClickイベントハンドラー

このプログラムはボタンがクリックされたときに実行されます。これをクリックイベントのイベントハンドラーと呼びます。

操作方法 画面を切り替える方法

イベントハンドラーを自動生成しないで、単にデザイン画面とコードエディタを切り替えたいだけの場合は、画面上部のタグをクリックします。

タブ

 

それではここに先ほどの画像を表示するプログラムを書いてみましょう。

private void Button1_Click(object sender, EventArgs e)
{
    this.BackgroundImage = Image.FromFile(@"C:\Windows\Web\Wallpaper\Windows\img0.jpg");
}

■リスト:ボタンをクリックするとフォームに C:\Windows\Web\Wallpaper\Windows\img0.jpg を表示する。

ここでは仮に「img0.jpg」という画像ファイルを指定しています。これはWindowsの標準の壁紙なので多くの環境に存在すると思います。画像はパソコンの中にある普通の画像ファイルであればだいたい使えるのでみなさんのお好みの画像を指定してください。指定できる画像の形式はbmp、gif、jpeg、png、tiffです。

実際に実行して試してみてください。

 

ここでのポイントは、

イベントが発生したらイベントハンドラに記述したプログラムが実行される

ということです。

 

3.イベントハンドラーの作り方

 

3−1.MouseHoverイベントのイベントハンドラー生成

 

今度はClickイベント以外のイベントもいくつか使ってみましょう。

コントロールには複数のイベントがあり、ボタンにもざっと数十個のイベントがあります。Clickイベントはその1つですが、ボタンの場合はもっともよく使うイベントなので特別扱いされており、デザイナー上でコントロールをダブルクリックするだけでそのイベントハンドラーが自動生成されるようになっています。

他のコントロールでもデザイナー上でダブルクリックするとコントロールごとにもっとも良く使うであろうイベントのイベントハンドラーが自動生成されるようになっています。

 

それ以外のイベントはプロパティウィンドウで該当するイベントをダブルクリックすることでイベントハンドラーを自動生成します。

プロパティウィンドウにはプロパティの一覧だけではなくイベントの一覧も表示されます。

 

それでは、ボタンのMouseHoverイベント (読み方:MouseHover = マウスホバー) のイベントハンドラーをプログラムしてみましょう。

MouseHoverイベントはマウスカーソルが上に来ると発生するイベントです。クリックしなくてもマウスカーソルを重ねるだけで発生します。

 

デザイン画面に切り替えてbutton1を選択してください。プロパティウィンドウでbutton1が選択されている状態で雷マークのイベントボタンをクリックしてください。

プロパティウィンドウにbutton1で使用できるイベントの一覧が表示されます。

説明どおりにやっている場合、Clickイベントにはイベントハンドラーが表示されていますが、それ以外のイベントには何も設定されていない状態になっています。

 ここでMouseHoverを探して、ダブルクリックしてください。

プロパティウィンドウでMouseHoverをダブルクリック

MouseHoverを選択するとMouseHoverのイベントハンドラーが次のように自動生成されます。

private void Button1_MouseHover(object sender, EventArgs e)
{

}

名前に「MouseHover」と入っているのでMouseHoverイベントのイベントハンドラーであるとわかります。

 

マウスが上に来たらボタンを光らせてみましょう。次のようにプログラムしてください。

Private Sub Button1_MouseHover(sender As Object, e As EventArgs) Handles Button1.MouseHover

    button1.BackColor = Color.Yellow;

End Sub

実行して、ボタンの上にマウスを持っていくと、ボタンの色が黄色くなるのが確認できます。

でも、マウスをボタンの外に移動しても色は元に戻りません。MouseHoverイベントが発生したらボタンの色を黄色にするというプログラムはしているのに、マウスが外にでていったら元の色に戻すというプログラムをしていないからです。マウスが外に出て行くときにはMouseLeaveイベント(読み方:MouseLeave = マウスリーブ)が発生しますからこのイベントを使いましょう。

 

3−2.MouseLeaveイベントのイベントハンドラー生成

やることは先ほどと同じです。デザイン画面に切り替えてbutton1を選択してください。プロパティウィンドウでbutton1が選択されている状態でイベントボタンをクリックしてください。

説明どおりにやっている場合、ClickイベントとMouseHoverイベントにはイベントハンドラーが表示されていますが、それ以外のイベントには何も設定されていない状態になっています。

プロパティウィンドウでMouseLeaveイベントを選択する

ここでMouseLeveを探して、ダブルクリックしてください。

そうするとMouseLeaveイベントハンドラーが生成されます。下記のようにプログラムしてください。

private void Button1_MouseLeave(object sender, EventArgs e)
{
    button1.BackColor = SystemColors.Control;
    button1.UseVisualStyleBackColor = true;
}

元の色がWindowsの視覚スタイルを利用したコントロール用の表示なので、黄色にするときより少しプログラムが増えています。私が説明したいのはイベントなので、このプログラムについては深入りしませんように。

これでマウスがボタンの外に出るとボタンの表示は元に戻ります。1度実行してみてください。

 

3−3.イベントハンドラーとイベントを結びつける仕組み

コントロールをダブルクリックしたり、プロパティウィンドウでイベントをダブルクリックすると自動的にイベントハンドラーのプログラムが生成されるのもう理解されたと思います。

ダブルクリックしたときにもう1つ目には見えない部分で変化が起きています。

それは、イベントとイベントハンドラーを結びつけるプログラムが自動的に作成されている点です。

たとえば、クリックイベントハンドラーとクリックイベントを結びつけるプログラムは次のようになります。

 
this.button1.Click += new System.EventHandler(this.Button1_Click);

このプログラムは普段表示されていないForm1.Designer.csというファイルの中に自動的に生成されます。

ソリューションエクスプローラーでForm1.csを展開するとこのファイルの中身を見ることができますが、このファイルの内容は自動的に編集されるので絶対に自分では変更しないでください。また、通常は見る必要もありません。

Form1.Designer.cs

 

3−4.イベントハンドラーの削除と再生成

イベントハンドラーを削除する場合は、自動生成されたイベントハンドラーだけを削除するとエラーになります。このことは第2回でも紹介しています。

たとえば、さきほどClickイベントのイベントハンドラーに下記のように背景に画像を表示するコードをプログラムしました。

private void Button1_Click(object sender, EventArgs e)
{
    this.BackgroundImage = Image.FromFile(@"C:\Windows\Web\Wallpaper\Windows\img0.jpg");
}

これがいらなくなった場合、private から } まで3行(空白行を含めると5行)を自分で削除してしまうとエラーになります。

どういうエラーになるか参考に画像を載せておきます。イベントハンドラーを削除してからデザイナー画面に切り替えるとこのエラーを見ることができます。

なお、このようなエラー画面はいろいろな要因で表示されますので、これを見たら即「イベントハンドラーを消してしまった」と判断するのではなく、表示されているメッセージ(下の画像の場合 『不明な名前 'Button1_Click' を処理できません。』というのが怪しいです)を見て判断するようにしてください。

もちろん、この状態でプログラムを実行しようとしてもエラーになります。

この理由はForm1.Designer.csにはイベントとイベントハンドラーを結びつけるプログラムが残っているのに、イベントハンドラーだけを削除してしまったため、結びつける対象をVisual Studioが見つけられなくなってしまったからです。

デザイナー画面くらい表示してくれても良さそうに思えますがVisual Studioのデザイナーはこの場合デザイナー画面も表示してくれなくなります。

この状態になってしまったら、元に戻すボタンを押してイベントハンドラーを削除前の状態に戻すのが一番です。一応、Form1.Designer.csのイベントとイベントハンドラーを結び付けているプログラムを自分で削除することでもエラーは解消できますが、さきほど説明したようにForm1.Designer.csを自分で編集することは控えるべきです。(でも、もしベテランの先輩があなたの隣に座ってこれを眺めていたら、その先輩はForm1.Designer.csを編集することでこれを解決してしまうかもしれません。何が危険で、どうすればよいかわかるだけの知識と経験があるのならばそれもOKです。)

元に戻すのがあまりにも大変な場合は、イベントハンドラーを自分で書いても良いです。このときイベントハンドラーの中身のプログラムはなくても良いです。とにかく結びつける対象のイベントハンドラーが存在していればよいのです。

 

エラーの説明が長くなってしまいました。初心者がよく困るポイントなので重点的に説明しました。

 

それでは、イベントハンドラーを削除する正しい手順はどのようなものでしょうか?

イベントハンドラーを削除するには、プロパティウィンドウで対象のイベントを右クリックして「リセット」を選択してください。

リセットを行うとイベントとイベントハンドラーの結びつけが解除され、Form1.Designer.cs内のプログラムも除去されます。

リセットする代わりにイベントハンドラーの名前をマウスとキーボードで除去して、Enterを押しても良いです。

 

イベントハンドラー内に何かプログラムが1行でもあればイベントハンドラーは削除されずに残りますが、それはもはやクリックしても動作しないのでイベントハンドラーではなく、いわばイベントハンドラーの残骸であり、名前がイベントハンドラー『風』のプログラムです。

操作方法 操作 - コントロールを削除すればイベントハンドラーはすべて解除されます

デザイナーでボタン自体を削除するなど、イベントを利用しているコントロール自体を削除すると、そのタイミングでそのコントロールのイベントハンドラーはすべてリセットされます。

 

一度リセットした後、やっぱりもう1度イベントハンドラーとして復活させたい場合は、右側の下矢印を展開しましょう。イベントハンドラーになれる候補の一覧が表示されます、この中にはイベントハンドラーの残骸も表示されますので、ここで選択しなおせば、改めてイベントハンドラーとして紐付けされクリックしたときに動作するようになります。

このときに残骸から選択するのではなく、新しいイベントハンドラーを作成した場合は、ダブルクリックするか、自分で新しいイベントハンドラーの名前を入力してEnterキーを押します。

private void MyClickEventHandler(object sender, EventArgs e)
{
    MessageBox.Show("これもクリックイベントのハンドラーになれるんです。");
}

 

残骸が残っている状態でダブルクリックすると元のイベントハンドラー名に連番がついた美しくない名前になりますので私はお勧めしません。

博士のワンポイントレッスン 「コピーしたサンプルが動作しない」
V太 V太:あれ、おかしいなぁ。クリックしても動かない・・・。
B子 B子:どうしたの?
V太 Webサイトに良さそうなサンプルを見つけたんで、コピー・貼り付けしてみたんだけどクリックしても何も起きないんだよ。
B子 あー。それね・・・。ほら、そこのButton1_Clickってイベントハンドラーなのよね。だからコピーするだけじゃ動かなくて、コピーした後で自分でプロパティウィンドウでイベントと結びつけないといけないんじゃないかしら。
V太 そういうことか!なるほどなるほど。イベントハンドラーはコピーしただけじゃイベントハンドラーになりませんよっと。
V太 でも、いちいちこれやるの面倒じゃないか?コピーだけで動けば楽なのに。
B子 まぁ、どうでボタンは自分で配置するしかないんだから、そのときにイベントハンドラーも結びつければいいんじゃないの?
博士 博士:ふむふむ。Webで公開されているサンプルの中にはコピーだけでイベントハンドラーが動くように特別な仕掛けがされているものもあるぞ。じゃが、そうでないものの方が多いから、自分でイベントハンドラーの結び付け暗いできるようにしておいたほうが良いのぉ。

 

4.フォームのイベント

 

フォームにも重要なイベントがいくつかあります。こういったことは「習うより慣れろ」です。どんどんいろんなイベントを使ってみましょう。

Windowsフォーム アプリケーションでは文字通りアプリケーションはフォームと共に開始し、フォームを閉じると終了します。

開始と終了に関連する主なイベントは次の4つです。

  イベント 読み方 発生するタイミング
イベント Load ロード フォームがはじめて表示される直前に発生します。
イベント Shown ショウン フォームが表示された直後に発生します。最初に動かしたい処理はこのタイミングでプログラムします。
イベント FormClosing フォームクロージング フォームが閉じる直前に発生します。この段階ではまだ閉じる操作をキャンセルできる場合があります。
イベント FormClosed フォームクローズド フォームが閉じた直後に発生します。最後に動かしたい処理はこのタイミングでプログラムします。

この他にボタンと同じようにClickイベントやMouseHoverイベントなどもあります。

参考:フォームのイベントの一覧

https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.form?view=netframework-4.8#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88

 

デザイン画面でフォームをダブルクリックするとLoadイベントのイベントハンドラーが生成されるため、プログラム開始時に何か処理を行いたい場合はLoadイベントに記述するプログラムが多いです。世の中の書籍やWebサイトを見てもおそらくほとんどの説明が開始時のプログラムを記述する場所としてLoadイベントを紹介していると思います。

しかし、Loadイベントはエラー発生時にエラーに気がつけない場合があるので、私はお勧めしていません。変わりにプログラム開始時に行いたい処理はShownに記述するようにしましょう。

ShownはVisual Studio 2005で追加されたイベントです。それより前のバージョンでは使用できません。

イベントハンドラーの生成方法はフォームでも同じです。Shownイベントのイベントハンドラーを生成させてみてください。

メモ メモ  -  もう説明済みのつもりですが、その方法がわからなければ・・・

たとえば、デザイン画面でフォームをクリックして、プロパティウィンドウでForm1が選択されている状態にします。その状態でプロパティウィンドウをイベントの一覧に切り替え、ShownをダブルクリックするとShownのイベントハンドラーが自動生成されます。

 

今は退屈ですが、Shownが発生したという出力だけ記述しておくことにしましょう。次のようにしてください。

private void Form1_Shown(object sender, EventArgs e)
{
    System.Diagnostics.Debug.WriteLine("Shownが発生しました。");
}

System.Diagnostics.Debug.WriteLine (読み方:System = システム、 Disgnostics = ダイアグナスティクス、Debug = デバッグ、WriteLine = ライトライン)はちょっとプログラムの確認をするときに便利な機能です。

これを実行すると、出力ウィンドウに指定したメッセージが表示されます。

出力ウィンドウはプログラムを実行中には右下に表示されています。「出力」というタブをクリックすることで他のウィンドウから切り替わります。

表示されていない場合は、[ウィンドウ]メニューでウィンドウレイアウトをリセットするか、[デバッグ]メニューの[ウィンドウ] - [出力] で表示できます。

実行時の出力ウィンドウ

このウィンドウにはいろいろな情報が表示されるため、出力ウィンドウの上側にある「出力元」という部分で内容を切り替えられます。Debug.WriteLineで書き込んだ内容は「デバッグ」を選択すると表示されます。

実行を停止すると下側に表示されています。

デザイン時の出力ウィンドウ

今度はFormClosingイベントとFormClosedイベントも利用して次のようにプログラムしてみてください。

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
    MessageBox.Show("フォームが閉じました。");
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    MessageBox.Show("フォームが閉じますよ。","FormClosingです。", MessageBoxButtons.OK, MessageBoxIcon.Information);
    MessageBox.Show("本当に閉じますよ!", "FormClosingです。", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}

これでプログラムを実行してから、フォームの右上の×ボタンでフォームを閉じようとすると3つのメッセージが順番に表示されます。

なお、イベントハンドラー同士はどちらが上にあっても下にあっても実行の順番には影響しません。フォームが閉じる直線に実行されるのがForm1_FormClosingであり、フォームが閉じた直後に実行されるのがForm1_Closedであるというイベントのタイミングに従って実行されます。

イベントハンドラー内のプログラムは基本的には上から下へと順番に実行されます。

 

せっかくなので最初の例で出した画像の表示もプログラムして見ましょう。Form1のShownイベントに次の通り記述してください。

private void Form1_Shown(object sender, EventArgs e)
{
    this.BackgroundImage = Image.FromFile(@"C:\Windows\Web\Wallpaper\Windows\img0.jpg");
}

これでプログラム開始時点から背景に画像が表示されているフォームの完成です。

画像ファイルは実際にみなさんのパソコンに入っているものを指定してください。

 

5.イベントドリブン での 自動処理

 

5−1.イベントドリブン

ここまでの説明で、「どこにプログラムしたらよいのか?」ということはだいたい分かっていただけたと思います。

処理したいタイミングのイベントハンドラーに記述するということです。

このようにイベントが発生するとそれに対応するイベントハンドラーに記述された処理が実行されるという仕組みを イベントドリブン と呼びます。日本語で言うところのイベント駆動という意味です。

Windowsフォームアプリケーションは基本的にイベントドリブンです。

 

5−2.Timerコンポーネント

Windowsフォームアプリケーションで、ゲームのように自動的にどんどん進行していくプログラムを作るには、Timer コンポーネント(読み方:Timer = タイマー)を使うのをお勧めします。

ここで言う「コンポーネント」とはコントロールと同じようなものなのだけど目には見えないものを指しています。TimerコンポーネントはButtonと同じようにツールボックスから配置できます。コンポーネントタブにあります。ただし実行時には目に見えません。

Timerコンポーネントには時間が経つと発生する Tick (読み方:Tick = ティック)というイベントがあります。どのくらい時間が経つとTickイベントが発生するかはIntervalプロパティ(読み方:Interval = インターバル)で設定できます。

ためしにフォームにTimerコンポーネントを配置してみましょう。コンポーネントは配置するとフォームの下側に表示されます。

Timerコンポーネントがフォームの下側に表示されている

次にプロパティウィンドウを使ってEnabledプロパティをTrueに(読み方:True = トゥルー)してください。

Enabledプロパティは有効/無効の状態を表しています。Trueとはプログラムでは良く出てくるとても重要な値で、「オン」や「有効」、「はい」などの肯定的な意味があります。つまり、EnabledプロパティをTrueにするということの意味はタイマーを有効にするという意味です。

なお、Trueの反対語はFalse(読み方:False = フォルス)です。こちらは「オフ」や「無効」、「いいえ」などの否定的な意味になります。

 

次にIntervalプロパティを 1000 に設定してください。これは1秒ごとにTickイベントを発生させることを意味します。もし、500に設定すると0.5秒後とにTickイベントが発生します。このような単位をミリ秒と呼びます。

もう1つ、FormのTextプロパティを 0 に設定してください。

まとめると設定すべきプロパティは次の通りです。

コントロール プロパティ
Timer1 Enabled True
  Interval 1000
Form Text 0

 

そして、Tickイベントハンドラーに次のように記述してみてください。

private void Timer1_Tick(object sender, EventArgs e)
{
    this.Text = (int.Parse(this.Text) + 1).ToString();
}

プログラムの内容は今回は説明しませんが、簡単に書くとこのプログラムは this.TextTickイベントが発生するたびに +1 するものです。フォームのプログラムではthisとはフォームのことで、そのTextプロパティはタイトルバーに表示されている一連の文字(たとえば「Form1」)を指します。Parse(読み方:Parse = パース)とかToString(読み方:ToString = トゥーストリング)は、値の型を変換するなどしているのですが、型については今後の入門講座で扱います。

実行すると、1秒ごとにタイトルバーがカウントアップしていきます。

メモ メモ

・・・と本文では簡単にプログラムを作っていますが、もうこの操作は自分のものにしたでしょうか?初めて使うコントロールやコンポーネントでも操作は同じです。

まだ入門講座第4回なので念のためこのサンプルを作る動画も掲載しておきます。でも、この時点ではこの操作は自分だけでできるようになっていることを期待しています。

TimerのTickイベントをプログラムする動画

 

このようにTimerを使うと自動的にどんどん処理が進んでいくというプログラムをイベントを使って記述することができます。

なお、TimerIntervalプロパティはミリ秒単位なので1秒の1000分の1の細かさで指定できますが、あまり精度は高くなく、細かい値を設定しても正確にその通り動いてくれるわけではありません。あくまでTickイベントを発生させるための目安の時間として使ってください。このTimerを利用して時間測定などはしないほうが良いです。