第20章 イベント駆動とタイマーアプリ
この章の目的
Section titled “この章の目的”この章では、書籍「作って覚える Visual C# 2022 デスクトップアプリ超入門」の Chapter 4.1「『タイマー』の作成」(書籍では Timer プロジェクト)を参照しながら、Timer コントロール を使ったアプリを作ります。
注意:本研修ではプロジェクト名を
CountdownTimerに変更しています書籍では
Timerという名前ですが、これは .NET のSystem.Windows.Forms.Timerなどの型名と衝突するため、本研修ではCountdownTimerに改名しました(詳しくは「本章で使用する環境」の直下にある補足を参照)。 書籍を読み進める際は プロジェクト名だけ読み替えて ください。作業中は本テキストの記載を優先してください。
第 19 章では、ボタンクリックという「利用者の操作」がきっかけのイベントを扱いました。
この章では、「時間の経過」 がきっかけのイベント(Tick イベント)を扱います。
これにより、イベント駆動の理解をさらに深めます。
ボタンクリックイベント 利用者がボタンを押す → 処理が動くタイマーイベント(Tick) 一定時間が経過する → 処理が動く書籍に手順がすべて書かれているので、書籍を手元に開いて手を動かしながら進めてください。 本テキスト側では、研修で意識してほしいポイントだけを押さえます。
この章を読むタイミング
Section titled “この章を読むタイミング”本章は 第 19 章を終えた後 に進めてください。
第 19 章で扱った「ボタンの Click イベント」「int.TryParse」「TextBox/Button 配置」を前提に、「時間きっかけ」のイベント(Tick)とフィールドによる状態保持を扱います。
並行受講では、第 19 章の課題が一段落したタイミングで本章に進みます。
この章で使用する書籍範囲
Section titled “この章で使用する書籍範囲”| 範囲 | 内容 |
|---|---|
| Chapter 4.1 | 「タイマー」の作成(書籍は Timer プロジェクト、本研修では CountdownTimer として作成) |
この章でできるようになること
Section titled “この章でできるようになること”この章を終えると、次のことができるようになります。
Timerコントロールを使って一定時間ごとに処理を動かせるStart()・Stop()メソッドでタイマーを制御できるTickイベントにイベントハンドラーを書ける- フォームクラスのフィールドにより、イベント呼び出しをまたいで値を保持できる
MessageBox.Showでメッセージダイアログを表示できる- ボタンクリックイベントとタイマーイベントの違いを説明できる
本章で使用する環境
Section titled “本章で使用する環境”| 項目 | 内容 |
|---|---|
| 開発環境 | Visual Studio 2022 |
| プロジェクト種類 | Windows フォーム アプリ |
| 対象フレームワーク | .NET 8 |
| プロジェクト名 | CountdownTimer(書籍では Timer、本研修では衝突回避のため改名) |
csproj の Nullable は disable に変更してください(第 1 章「1-1」参照)
補足:プロジェクト名を
TimerからCountdownTimerに改名している理由書籍ではこのアプリのプロジェクト名を
Timerとしていますが、本研修ではCountdownTimerに改名しています。理由は 「プロジェクト名 = 既定の名前空間」となるため、
Timerという名前は .NET の以下の型と衝突するからです。
名前空間.型 用途 System.Windows.Forms.Timer本章で使う Forms 用タイマー(UI スレッド) System.Timers.Timer汎用タイマー System.Threading.Timerスレッドベースのタイマー Visual Studio の Designer が自動生成するコードは完全修飾名(
System.Windows.Forms.Timer)で書き出すため、書籍どおりに写経する分にはコンパイルが通ります。しかし、自分でTimer t = new Timer();のように書こうとすると、次のような コンパイルエラー(CS0118) に遭遇します。error CS0118: 'Timer' は名前空間ですが、型として使用されています名前空間
Timerがそのまま型名Timerと衝突するためです。第 20 章は 自分でTimerを扱う章 であり、この罠を踏みやすい構成なので、本研修では最初からCountdownTimerに改名して回避しています。プロジェクト名と既定の名前空間の関係(プロジェクト名がそのままトップレベル namespace になる)は、第 1 章・第 7 章でも触れた仕組みです。「.NET にある型名(
Timer、Form、Math、Consoleなど)を、プロジェクト名や namespace に使わない」というのは現場でも有効な指針です。
作業前チェック
Section titled “作業前チェック”- 第 19 章を Git に提出済みである
- 書籍 Chapter 4.1 のページを開ける状態である
- フォームに
TextBox・Buttonを配置し、クリックイベントを書ける(第 19 章) -
int.TryParseを使える(第 15 章) - フィールドとローカル変数の違いを思い出せる(第 7 章のスコープ)
20-1 Timer コントロールの位置づけ
Section titled “20-1 Timer コントロールの位置づけ”Timer コントロールは、ツールボックスに並んでいる他のコントロール(Label・Button など)と少し違う点があります。
| 観点 | Label・Button など | Timer |
|---|---|---|
| 画面に表示される? | される | されない(コンポーネントトレイに置かれる) |
| 利用者から見える? | 見える | 見えない |
| 役割 | 入出力 | 一定時間ごとに処理を動かす |
Timer をフォームに配置すると、フォーム下部の コンポーネントトレイ という領域に置かれます。
画面上には表示されませんが、コードから普通のコントロールと同じように扱えます。
20-2 Timer の主なメンバー
Section titled “20-2 Timer の主なメンバー”書籍 4.1 で実際に使うのは、次のあたりです。
| メンバー | 種類 | 役割 |
|---|---|---|
Interval | プロパティ | 何ミリ秒ごとに Tick を発生させるか(1000 で 1 秒) |
Enabled | プロパティ | 動作中なら true(Start()/Stop() でも切り替わる) |
Start() | メソッド | タイマーを開始する |
Stop() | メソッド | タイマーを停止する |
Tick | イベント | Interval ごとに発生 |
第 8 章で学んだとおり、() が付くのが メソッド、付かないのが プロパティ です。
20-3 ボタンと Timer のイベントの違い
Section titled “20-3 ボタンと Timer のイベントの違い”ボタンクリック (Click) 利用者の操作 → Click イベント → ハンドラー実行 → 待機
Timer (Tick) 時間経過 → Tick イベント → ハンドラー実行 → 時間経過 → Tick → ...(繰り返し)Tick は タイマーが動いている間、Interval ごとに繰り返し発生 します。
Stop() を呼ぶまで止まりません。
20-4 フィールドで状態を覚えておく
Section titled “20-4 フィールドで状態を覚えておく”タイマーアプリでは、「経過時間」「終了時間」のような値を、複数の Tick 呼び出しをまたいで覚えておく 必要があります。
第 7 章で学んだとおり、メソッド内で宣言したローカル変数はそのメソッドが終わると消えてしまいます。 そこで、フォームクラスのフィールド として宣言します。
public partial class FormTimer : Form{ int endTime; // 終了時間(クラスのフィールド) int nowTime; // 経過時間(クラスのフィールド)
public FormTimer() { InitializeComponent(); }
private void buttonStart_Click(object sender, EventArgs e) { // ローカルではなく、フィールド endTime に値を入れる }
private void timerControl_Tick(object sender, EventArgs e) { // 前回の Tick 時の nowTime をそのまま参照できる }}| 場所 | 例 | 寿命 |
|---|---|---|
| メソッドの中 | int valueLeft;(第 19 章 SimpleCalc) | そのメソッドの実行中だけ |
| クラスの中 | int endTime;(本章 CountdownTimer) | フォームが存在する間ずっと |
ボタンクリックの中で値を入れて、別のイベント(Tick)で読み出す ── この使い方で、フィールドの存在意義 がはっきり分かります。
補足:
privateを付けるかどうか書籍ではフィールド宣言に
privateなどのアクセス修飾子が省略されています。 省略時はprivate扱い(クラスの中だけから使える)になるので、書籍の書き方で問題ありません。 第 10 章で学んだ「外から守る」考え方とつながります。
20-5 MessageBox.Show でメッセージダイアログを出す
Section titled “20-5 MessageBox.Show でメッセージダイアログを出す”書籍 4.1 では、設定時間に達すると メッセージダイアログ で知らせます。
これは MessageBox クラスの Show メソッドで表示できます。
MessageBox.Show("時間になりました!");MessageBox は static class(第 8 章)なので、new せずにクラス名から直接呼び出せます。
これも、これまでに学んだ知識のままです。
よくあるつまずき
Section titled “よくあるつまずき”| つまずき | 原因 | 対応 |
|---|---|---|
Timer をフォームに置いても画面に出ない | コンポーネントトレイに置かれる仕様 | 画面外のコンポーネントトレイで選択 |
Tick が一度しか動かない | Stop() を間違えた場所で呼んでいる | Stop() を呼ぶ条件を見直す |
nowTime がリセットされない | フィールドだから前回の値が残る | Start ボタンの中で nowTime = 0; で初期化 |
| 経過時間が変な値になる | ローカル変数で宣言してしまった | クラスのフィールドにする |
Interval を変えても秒数が変わらない | 単位はミリ秒 | 1000 で 1 秒、500 で 0.5 秒 |
Start() を Start と書いてエラー | メソッドは () が必要 | timerControl.Start(); |
学んだことチェック
Section titled “学んだことチェック”-
Timerコントロールが画面に表示されないことを説明できる -
Intervalプロパティで時間間隔を設定できる -
Start()・Stop()でタイマーを制御できる -
TickイベントがIntervalごとに繰り返し発生することを説明できる - フォームクラスのフィールドで状態を保持できる
-
MessageBox.Showでメッセージを表示できる - ボタンクリックイベントとタイマーイベントの違いを説明できる
研修の進め方によっては、隣の人またはチーム内で説明確認を行います。
次の内容を、自分の言葉で説明してください。
Timerコントロールが画面に表示されないのはなぜですか。Intervalの単位は何ですか。Tickイベントは、いつ何回発生しますか。endTimeやnowTimeをローカル変数ではなくフィールドにする理由は何ですか。MessageBox.Showはnewしていませんが、なぜ呼び出せますか(第 8 章を思い出して)。- ボタンクリックイベントと
Tickイベントの違いを 2 つ以上挙げてください。
書籍 Chapter 4.1 を参照しながら作業します。 画面操作・部品配置の細かな手順は書籍に書かれているので、書籍を手元に開いて進めてください。
本章の進め方
Section titled “本章の進め方”| 段階 | 目安時間 | 内容 |
|---|---|---|
| ① 準備 | 10 分 | ペア確認 + 課題確認(評価対象外) |
| ② ソロ作業 | 40 分 | タイマーで計測。タイマー時点の commit が唯一の評価対象 |
| ③ チーム時間 | 講師指定の発表開始時刻まで | レビュー + 発表者選出 + 実装続行(任意)。発表開始時刻は厳守 |
提出ルール(タイマー方式)
タイマー時点の commit が唯一の評価対象です。タイマー後の書き足しは評価されません。 コミットメッセージ形式:
Chapter20 タイマー提出: <どこまで完成> / <詰まったポイント>(なければ「特になし」) 例:Chapter20 タイマー提出: 必須20-1完成、20-2 観察済み / Interval 単位が分からなくなった
提出方法:Git が使えないときはサーバへコピー
Git の調子が悪いときは、講師の指示に従って
CountdownTimerプロジェクトのフォルダを提出先サーバへコピーしてください。コピー後、提出先フォルダにエクスプローラーの右クリック→新規作成→テキスト ドキュメントで提出メモ.txt(「どこまで完成」「詰まったポイント」を記載)を作成し、コミットメッセージの代わりにします。
タイマー後のチーム時間の使い方
レビュー・発表者選出・実装続行(任意)を自由配分してください。発表開始時刻は厳守です。
課題 20-1 書籍 Chapter 4.1 を参照して CountdownTimer アプリを完成させる
Section titled “課題 20-1 書籍 Chapter 4.1 を参照して CountdownTimer アプリを完成させる”書籍 4.1 を参照して、CountdownTimer プロジェクトを完成させてください(書籍では Timer プロジェクトという名前ですが、本研修では名前空間衝突を避けるため CountdownTimer に改名しています)。
仕様(書籍と同じ。プロジェクト名のみ改名)
- プロジェクト名:
CountdownTimer(書籍のTimerを改名) - フォームに
TextBox(時間設定用)、TextBox(残り時間表示用)、Button(開始)、Timerコントロールを配置 - 「開始」ボタンを押すと、設定した時間からカウントダウンが始まる
- 残り時間が
0になったら、MessageBoxで「時間になりました!」と表示
確認すること
-
Timerコントロールがコンポーネントトレイに置かれている -
Tickイベントにイベントハンドラーが書かれている - フィールド(
endTime・nowTimeなど)が定義されている - 「開始」ボタンでカウントダウンが始まる
- 残り時間が画面に表示される
-
0になるとMessageBoxが表示される - csproj が
<Nullable>disable</Nullable>
課題 20-2 処理が動くタイミングを確認する
Section titled “課題 20-2 処理が動くタイミングを確認する”完成した Timer アプリを実行し、「いつ・何がきっかけで・どの処理が動くのか」 を観察してください。
確認シナリオ
- アプリ起動直後 → タイマーは止まっている(
Tickは発生しない) - 「開始」ボタンを押した瞬間 →
buttonStart_Clickが動く +Start()が呼ばれる - ボタンを押した後 →
Intervalミリ秒ごとにTickが発生する - 残り時間が
0になる →Stop()が呼ばれてタイマー停止 +MessageBox表示 - もう一度「開始」を押す → カウンターがリセットされて再スタート
確認すること
- 1〜5 の各タイミングで何が起きるか、自分の言葉で説明できる
- フィールド
nowTimeがどう変化するか追える - 「開始」を 2 回押しても正しくリセットされる(
nowTime = 0;のおかげ)
観察のヒント
Visual Studio のデバッガーで
Tickハンドラー内にブレークポイントを置くと、Intervalごとに止まることが体感できます。 ブレークポイントの使い方は、次章(第 21 章)で扱います。
課題 20-3 タイマーの動作を少し変更する
Section titled “課題 20-3 タイマーの動作を少し変更する”完成した Timer アプリに、小さな変更を加えてみてください。 1 つだけでよい ので、自分で選んで実装してください。
変更例(どれか 1 つ)
| 変更内容 | ヒント |
|---|---|
Interval を変える | 100 にすると 0.1 秒ごとに Tick が動く |
| 表示形式を変える | 00:05 のように String.Format や ToString("00") を使う |
| 「停止」ボタンを追加 | クリックで timerControl.Stop(); を呼ぶ |
| 開始中は「開始」ボタンを無効化 | buttonStart.Enabled = false; で押せなくする |
| 終了メッセージを変える | MessageBox.Show の引数を変える |
確認すること
- 変更前と変更後の動きの違いを説明できる
- 何のプロパティ・メソッドを変えたかを説明できる
課題 20-4 フィールドとローカル変数の違いを確認する
Section titled “課題 20-4 フィールドとローカル変数の違いを確認する”endTime・nowTime を わざとローカル変数に変えて、動かなくなることを確認してください。
そのあと 元のフィールドに戻して、ちゃんと動くことを再確認してください。
手順
endTime・nowTimeのクラスフィールド宣言をコメントアウトbuttonStart_Clickの中でint endTime; int nowTime;のようにローカルで宣言timerControl_Tickの中でも同様にローカル宣言してみる- ビルドエラーまたは実行時のおかしな挙動を確認
- 元に戻して、フィールドの存在意義を説明できるようにする
確認すること
- ローカル変数にすると動かない理由を説明できる
- フィールドだとなぜ動くか説明できる
- 第 7 章「変数のスコープ」と関連付けて理解できている
提出前チェックリスト
Section titled “提出前チェックリスト”- プロジェクト名が
CountdownTimer(書籍のTimerから改名) - csproj が
<Nullable>disable</Nullable> -
Timerコントロールが配置されている - フィールドで状態を保持している
-
Start()・Stop()・Tickを使えている -
MessageBox.Showを使えている -
bin・obj・.vsフォルダが Git 管理に入っていない - タイマー時点で commit 済み(または
提出メモ.txtを書いた)
Git への提出
Section titled “Git への提出”git statusgit add .git commit -m "Chapter20 タイマー提出: 必須20-1完成、20-2観察済み / 特になし"git push origin mainGit が使えないときは、上記コミットの代わりに CountdownTimer プロジェクトのフォルダを提出先サーバへコピーし、コピー先に 提出メモ.txt を作成してください(演習課題の「提出方法:Git が使えないときはサーバへコピー」参照)。
Git の詳しい操作は、付録 C「Git のインストールと提出ルール」 を参照してください。
この章のまとめ
Section titled “この章のまとめ”Timerコントロールは画面に表示されないコンポーネントIntervalごとにTickイベントが発生する(時間がきっかけのイベント)Start()でカウント開始、Stop()で停止- 複数のイベント呼び出しをまたいで値を保持するには フォームクラスのフィールド を使う
MessageBox.Showでメッセージダイアログを出せる(MessageBoxは静的クラス、第 8 章)- ボタンクリックイベントと
Tickイベントの違いを通じて、イベント駆動の理解が深まる
次章では、Windows フォームアプリの デバッグ操作 を学びます。 ブレークポイントを置いてプログラムを途中で止め、変数の中身を確認したり、1 行ずつ実行して動きを追ったりできるようになります。 これは Forms に限らず、すべての C# プログラムで使える強力な技術です。