Skip to content

第20章 イベント駆動とタイマーアプリ

この章では、書籍「作って覚える Visual C# 2022 デスクトップアプリ超入門」の Chapter 4.1「『タイマー』の作成」(書籍では Timer プロジェクト)を参照しながら、Timer コントロール を使ったアプリを作ります。

注意:本研修ではプロジェクト名を CountdownTimer に変更しています

書籍では Timer という名前ですが、これは .NET の System.Windows.Forms.Timer などの型名と衝突するため、本研修では CountdownTimer に改名しました(詳しくは「本章で使用する環境」の直下にある補足を参照)。 書籍を読み進める際は プロジェクト名だけ読み替えて ください。作業中は本テキストの記載を優先してください。

第 19 章では、ボタンクリックという「利用者の操作」がきっかけのイベントを扱いました。 この章では、「時間の経過」 がきっかけのイベント(Tick イベント)を扱います。 これにより、イベント駆動の理解をさらに深めます。

ボタンクリックイベント 利用者がボタンを押す → 処理が動く
タイマーイベント(Tick) 一定時間が経過する → 処理が動く

書籍に手順がすべて書かれているので、書籍を手元に開いて手を動かしながら進めてください。 本テキスト側では、研修で意識してほしいポイントだけを押さえます。


本章は 第 19 章を終えた後 に進めてください。 第 19 章で扱った「ボタンの Click イベント」「int.TryParse」「TextBox/Button 配置」を前提に、「時間きっかけ」のイベント(Tick)とフィールドによる状態保持を扱います。

並行受講では、第 19 章の課題が一段落したタイミングで本章に進みます。


範囲内容
Chapter 4.1「タイマー」の作成(書籍は Timer プロジェクト、本研修では CountdownTimer として作成)

この章でできるようになること

Section titled “この章でできるようになること”

この章を終えると、次のことができるようになります。

  • Timer コントロールを使って一定時間ごとに処理を動かせる
  • Start()Stop() メソッドでタイマーを制御できる
  • Tick イベントにイベントハンドラーを書ける
  • フォームクラスのフィールドにより、イベント呼び出しをまたいで値を保持できる
  • MessageBox.Show でメッセージダイアログを表示できる
  • ボタンクリックイベントとタイマーイベントの違いを説明できる

項目内容
開発環境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 にある型名(TimerFormMathConsole など)を、プロジェクト名や namespace に使わない」というのは現場でも有効な指針です。


  • 第 19 章を Git に提出済みである
  • 書籍 Chapter 4.1 のページを開ける状態である
  • フォームに TextBoxButton を配置し、クリックイベントを書ける(第 19 章)
  • int.TryParse を使える(第 15 章)
  • フィールドとローカル変数の違いを思い出せる(第 7 章のスコープ)

20-1 Timer コントロールの位置づけ

Section titled “20-1 Timer コントロールの位置づけ”

Timer コントロールは、ツールボックスに並んでいる他のコントロール(LabelButton など)と少し違う点があります。

観点LabelButton などTimer
画面に表示される?されるされない(コンポーネントトレイに置かれる)
利用者から見える?見える見えない
役割入出力一定時間ごとに処理を動かす

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("時間になりました!");

MessageBoxstatic class(第 8 章)なので、new せずにクラス名から直接呼び出せます。 これも、これまでに学んだ知識のままです。


つまずき原因対応
Timer をフォームに置いても画面に出ないコンポーネントトレイに置かれる仕様画面外のコンポーネントトレイで選択
Tick が一度しか動かないStop() を間違えた場所で呼んでいるStop() を呼ぶ条件を見直す
nowTime がリセットされないフィールドだから前回の値が残るStart ボタンの中で nowTime = 0; で初期化
経過時間が変な値になるローカル変数で宣言してしまったクラスのフィールドにする
Interval を変えても秒数が変わらない単位はミリ秒1000 で 1 秒、500 で 0.5 秒
Start()Start と書いてエラーメソッドは () が必要timerControl.Start();

  • Timer コントロールが画面に表示されないことを説明できる
  • Interval プロパティで時間間隔を設定できる
  • Start()Stop() でタイマーを制御できる
  • Tick イベントが Interval ごとに繰り返し発生することを説明できる
  • フォームクラスのフィールドで状態を保持できる
  • MessageBox.Show でメッセージを表示できる
  • ボタンクリックイベントとタイマーイベントの違いを説明できる

研修の進め方によっては、隣の人またはチーム内で説明確認を行います。

次の内容を、自分の言葉で説明してください。

  1. Timer コントロールが画面に表示されないのはなぜですか。
  2. Interval の単位は何ですか。
  3. Tick イベントは、いつ何回発生しますか。
  4. endTimenowTime をローカル変数ではなくフィールドにする理由は何ですか。
  5. MessageBox.Shownew していませんが、なぜ呼び出せますか(第 8 章を思い出して)。
  6. ボタンクリックイベントと Tick イベントの違いを 2 つ以上挙げてください。

書籍 Chapter 4.1 を参照しながら作業します。 画面操作・部品配置の細かな手順は書籍に書かれているので、書籍を手元に開いて進めてください。

段階目安時間内容
① 準備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 イベントにイベントハンドラーが書かれている
  • フィールド(endTimenowTime など)が定義されている
  • 「開始」ボタンでカウントダウンが始まる
  • 残り時間が画面に表示される
  • 0 になると MessageBox が表示される
  • csproj が <Nullable>disable</Nullable>

課題 20-2 処理が動くタイミングを確認する

Section titled “課題 20-2 処理が動くタイミングを確認する”

完成した Timer アプリを実行し、「いつ・何がきっかけで・どの処理が動くのか」 を観察してください。

確認シナリオ

  1. アプリ起動直後 → タイマーは止まっている(Tick は発生しない)
  2. 「開始」ボタンを押した瞬間 → buttonStart_Click が動く + Start() が呼ばれる
  3. ボタンを押した後 → Interval ミリ秒ごとに Tick が発生する
  4. 残り時間が 0 になる → Stop() が呼ばれてタイマー停止 + MessageBox 表示
  5. もう一度「開始」を押す → カウンターがリセットされて再スタート

確認すること

  • 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.FormatToString("00") を使う
「停止」ボタンを追加クリックで timerControl.Stop(); を呼ぶ
開始中は「開始」ボタンを無効化buttonStart.Enabled = false; で押せなくする
終了メッセージを変えるMessageBox.Show の引数を変える

確認すること

  • 変更前と変更後の動きの違いを説明できる
  • 何のプロパティ・メソッドを変えたかを説明できる

課題 20-4 フィールドとローカル変数の違いを確認する

Section titled “課題 20-4 フィールドとローカル変数の違いを確認する”

endTimenowTimeわざとローカル変数に変えて、動かなくなることを確認してください。 そのあと 元のフィールドに戻して、ちゃんと動くことを再確認してください。

手順

  1. endTimenowTime のクラスフィールド宣言をコメントアウト
  2. buttonStart_Click の中で int endTime; int nowTime; のようにローカルで宣言
  3. timerControl_Tick の中でも同様にローカル宣言してみる
  4. ビルドエラーまたは実行時のおかしな挙動を確認
  5. 元に戻して、フィールドの存在意義を説明できるようにする

確認すること

  • ローカル変数にすると動かない理由を説明できる
  • フィールドだとなぜ動くか説明できる
  • 第 7 章「変数のスコープ」と関連付けて理解できている

  • プロジェクト名が CountdownTimer(書籍の Timer から改名)
  • csproj が <Nullable>disable</Nullable>
  • Timer コントロールが配置されている
  • フィールドで状態を保持している
  • Start()Stop()Tick を使えている
  • MessageBox.Show を使えている
  • binobj.vs フォルダが Git 管理に入っていない
  • タイマー時点で commit 済み(または 提出メモ.txt を書いた)

Terminal window
git status
git add .
git commit -m "Chapter20 タイマー提出: 必須20-1完成、20-2観察済み / 特になし"
git push origin main

Git が使えないときは、上記コミットの代わりに CountdownTimer プロジェクトのフォルダを提出先サーバへコピーし、コピー先に 提出メモ.txt を作成してください(演習課題の「提出方法:Git が使えないときはサーバへコピー」参照)。

Git の詳しい操作は、付録 C「Git のインストールと提出ルール」 を参照してください。


  • Timer コントロールは画面に表示されないコンポーネント
  • Interval ごとに Tick イベントが発生する(時間がきっかけのイベント)
  • Start() でカウント開始、Stop() で停止
  • 複数のイベント呼び出しをまたいで値を保持するには フォームクラスのフィールド を使う
  • MessageBox.Show でメッセージダイアログを出せる(MessageBox は静的クラス、第 8 章)
  • ボタンクリックイベントと Tick イベントの違いを通じて、イベント駆動の理解が深まる

次章では、Windows フォームアプリの デバッグ操作 を学びます。 ブレークポイントを置いてプログラムを途中で止め、変数の中身を確認したり、1 行ずつ実行して動きを追ったりできるようになります。 これは Forms に限らず、すべての C# プログラムで使える強力な技術です。