【C#】時計を作ってみよう
Visual Studioの起動
Visual Studioを起動し「新しいプロジェクトの作成」をクリックします。
Windows Formsアプリケーションの作成
次の画面で検索欄に「windows forms」と入力し、「Windows フォームアプリケーション(.NET Framework)」をダブルクリックします。
プロジェクトの作成
プロジェクト名に適当な名前を入力し、「作成」をクリックします。
Labelの配置
ツールボックスから「Label」をフォーム上へドラッグ&ドロップします。
フォントサイズの変更とテキストの設定
フォントサイズを大きく(24pt程度)して、テキストを「00:00:00」と入力します。
適当に整える
ラベルの位置とフォームの大きさを適当に調整します。
フォームをダブルクリック
ラベルではなくフォーム部分をダブルクリックすると以下のようにイベントが追加されコード入力画面になります。
Form1_Loadというのがイベントで、フォームがロードされた時に処理されます。
ラベルに現在時刻を表示するコードを書く
以下のように記述して再生ボタンを押すと現在時刻が表示されます。
しかし、このままでは、フォームロード時の時刻を表示するだけで、動きません。
private void Form1_Load(object sender, EventArgs e) { label1.Text = DateTime.Now.ToString("HH:mm:ss"); }
延々と繰り返すようにする
延々と繰り返し処理をするには「while」を使用します。
しかし、このままでは、フォームロードイベントから抜け出せず、フォームが表示されなくなります。
private void Form1_Load(object sender, EventArgs e) { while (true) { label1.Text = DateTime.Now.ToString("HH:mm:ss"); } }
抜け出すためには
ラベルの表示処理とフォームロードのイベントを別々に処理したいわけです。
こういう場合、別Threadで処理を行う事で並列処理を行う事ができます。
今回は簡単のためにBackgroundWorkerというクラスを使います。
private void Form1_Load(object sender, EventArgs e) { BackgroundWorker bw = new BackgroundWorker(); bw.DoWork += Bw_DoWork; bw.RunWorkerAsync(); } private void Bw_DoWork(object sender, DoWorkEventArgs e) { while (true) { label1.Text = DateTime.Now.ToString("HH:mm:ss"); } }
エラーが出るんですけど
このまま実行すると「System.InvalidOperationException」というエラーが発生します。これは、フォームと別のスレッドからフォームの内容をいじろうとした場合、特別な処理が必要になることを意味します。
Invokeを使う
これはたぶん覚えないとしょうがないと思うのですが、Invokeを使うとエラー発生を抑えることができます。
しれっとdelegateってのも出てきていますが、これも呪文みたいなものだと思ってください。
delegate void LC(); private void Form1_Load(object sender, EventArgs e) { BackgroundWorker bw = new BackgroundWorker(); bw.DoWork += Bw_DoWork; bw.RunWorkerAsync(); } private void Bw_DoWork(object sender, DoWorkEventArgs e) { LC labelChange = new LC(LabelChange); while (true) { Invoke(labelChange); } } private void LabelChange() { label1.Text = DateTime.Now.ToString("HH:mm:ss"); }
もっとスマートに書けないの?
ただラベルのテキスト変更するだけでこんなにだらだらと長くなってしまうのは嫌ですよね。
C#では、ラムダ式といってdelegateを簡単に書く事ができます。
private void Form1_Load(object sender, EventArgs e) { BackgroundWorker bw = new BackgroundWorker(); bw.DoWork += delegate (object s, DoWorkEventArgs ev) { while (true) { Invoke((MethodInvoker)delegate () { label1.Text = DateTime.Now.ToString("HH:mm:ss"); }); } }; bw.RunWorkerAsync(); }