今回は「クリックした場所に文字を描画」するという、これまた単体では面白くもなんともないプログラムを作成します。前回の最後のほうで語ったように「世の中に存在する面白いものや役に立つものは、それだけではつまらなく面白くないものから作られている」のです。ただ本当に面白くないと誰も見てくれないので、面白くする努力はしています。

今回作成するものはフォーム上をクリックするとそこに「鳩」という文字が描画されるプログラムです。しかもゲームを作ろうとするとクライアント領域に画像や文字を描画するテクニックはよく使うので、将来的には役にたつものであると自負しております。

では、やってみましょう。

前回の本当に鳩でも分かるC#講座をやってみるでつかったものを使うのもありです。この場合、ボタンは必要ないので削除してしまいましょう。ボタンを選択してDeleteキーをおせばボタンは消えます。以下のコードも消してしまいましょう。

まず、クリックした場所に文字を描画したいのであれば、クリックされたことを検知する仕組みを作らなければらないのですが、これは難しく考える必要がありません。以下のコードに4行追加するだけです。他にもMouseDownイベントを使う方法もあるのですが、コピペで簡単にできるのでこの方法を採用します。

OnMouseDownはマウスボタンがクリックされたときに呼び出されるのですが、なかにはbase.OnMouseDown(e);としか書かれていません。これではクリックしてもなにもおきないので以下のように書いてみます。

最初にひとこと言っておくと、これはあまりいい方法ではありません

最初にクリックされたときに表示したい文字列をフィールド変数として定義しています。これはあとになって「鳩」ではなく「烏」(カラス)にしたくなるかもしれません。そのときはこの部分を「烏」に変更だけで対応できます。いまのところ「鳩」は一箇所にしかないのですが、そのうち「鳩」が何度も使われるかもしれません。そんなときに「鳩」をぜんぶ「烏」に置き換えなければなりません。しかし

人間とはミスをする動物である!

だから一箇所だけ変更すればよいようにわざわざstrというフィールド変数をつくっているのです。

次に表示させたい文字列のフォントを Font strFont = new Font(“MS ゴシック”, 20);としています。最初の引数を”MS ゴシック”、二番目の引数を20にすることでフォントの大きさを20ポイントに指定しています。これもあとになって気が変わるかもしれないので、ここを変えれば対応できるようにフィールド変数にしてあります。

文字をフォーム上に描画するのであればGraphicsオブジェクトを取得しなければなりません。それがこの部分です。

Graphicsを取得したらこれをつかって文字を描画します。文字はどこにどんな色でどのフォントを使って描画するのか? それは以下のように指定すれば、クリックされた位置に黒でMS ゴシック 20ポイントで描画されます。

終わったらgraphicsを破棄します。IDisposable インターフェースを実装するオブジェクトは、使い終わった時に必ず Dispose メソッドを呼び出して破棄しなければならないと決められています。これを忘れてしまうとパソコンが大爆発するような大惨事にはなりませんが、「IDisposable インターフェースを実装するオブジェクトは、使い終わった時に必ず Dispose メソッドを呼び出して破棄しなければならない」と決められているのでここは従っておきましょう。

これで完成と言いたいのですが、実はこのプログラムには欠陥があります。

実際に実行してクリックしてください。クリックされた場所に「鳩」という文字が描画されるはずです。クリックされた位置が「鳩」の真ん中にならず右上になるのはここではたいした問題ではありません。フォームのサイズを小さく変更してみてください。また右上にある「_」ボタンをおして最小化させたあともどのサイズに戻してください。描画されていた鳩が消えてしまいます。鳩はいったいとこへ飛んでいってしまったのでしょうか?

Winndowsを使っていると複数のウィンドウが重なる場合があります。その場合、下にあるウィンドウの一部が上にあるウィンドウによって隠されてしまいます。そして下にあるウィンドウの一部をクリックすると上下関係がかわって隠れていた部分がみえるようになります。

まあこんなことはWinndowsを使っている方であれば誰でも知っていることですが、なぜこんなことが可能になるのでしょうか? それは隠れていた部分が再描画されるからです。そして再描画されるときにクリックして描画した「鳩」も再描画してほしいのですが、そのための処理はどこにも書かれていないので鳩は消えてしまうのです。

つまりウィンドウが再描画されるときに鳩も復元する処理が必要です。

Form1クラスのなかにOnPaint(PaintEventArgs e)を追加してください。すると全体はこうなるはずです。namespace WinFormsApp1の部分は省略しています。

OnPaintのなかで再描画の処理がおこなわれます。ここに鳩を復元する処理を書けばよいですね。ではそのようにしてみましょう。

まずクリックされたら鳩をどこに描画するのかを記憶させる必要があります。そんなときに便利なのがListクラスです。

そしてOnMouseDown(MouseEventArgs e)の部分を以下のように書き換えます。

クリックされたらクリックされた座標をPointsのなかに格納していきます。そしてそのあとthis.Invalidate();とありますが、これはフォームに対して再描画を要求する(正確な表現ではありませんが、だいたいそういう意味です)ものです。するとさきほど作成したOnPaintの部分がが実行されます。ここにPointsのなかに保存していた座標の部分に「鳩」という文字が描画されるのです。

foreachは要素をひとつずつ取り出して処理をおこないます。Pointsのなかに保存されている座標は全部でいくつあるのかを考える必要はないので非常にありがたいです。

実際にこれを実行してクリックして「鳩」を描画したあとフォームを最小化したり、他のウィンドウの後ろに隠してふたたび表示させてみてください。今度は鳩は消えずに残っています。

当たり前の話かもしれませんが、プログラムを終了してしまったら鳩は消えてしまいます。

それではプログラムを終了しても鳩が消えない方法を考えてみましょう。ひとつの方法としてプログラムが終了するときに鳩の座標をファイルとして保存しておき、プログラムが起動したら自動的にこれを読み取って鳩を復元するという方法が考えられます。

Form1.csというファイルの上のほうに

と書かれているはずです。この下に以下の2行を追加してください。

まずプログラムが終了するときにファイルを保存したいので、ファイルを保存する場所を決めます。Application.UserAppDataPathはユーザーのアプリケーション データのパスを取得します。

もし自分一人でしか使っていないパソコンであればユーザーのアプリケーション データのパスではなく、このプログラムの実行ファイルがあるパスでもかまいません。ユーザーのアプリケーション データのパスはプロジェクト名や実行ファイルのバージョンを変えると変わってしまうのでこちらのほうがわかりやすいかもしれません。その場合はApplication.UserAppDataPathではなくApplication.StartupPathにします。当然のことながらApplication.UserAppDataPathとApplication.StartupPathは別の場所です。

そしてprotected override void OnClosed(EventArgs e)を追加してください。

それから新しいクラスをつくります。簡単なクラスなのでメニューからクラスの追加を選ばずにForm1.csの一番したに書いてよいと思います。Form1が存在する名前空間と同じにしておきましょう。

ではForm1クラスのOnClosedメソッドを以下のように編集します。

これでファイルが保存されます。もし保存場所としてApplication.StartupPath~を選択したのであればbin\Debug\net5.0-windowsフォルダのなかを調べてみてください。data.xmlというファイルが生成されているはずです。

実際にファイルを開いてみると数字が異なっているとは思いますが、こんな内容になっていると思います。

さてでは次回プログラムを起動したとき、これをもとに鳩を復元できるようにしてみましょう。

まず本当に上記で保存したファイルは存在するのでしょうか? Debugフォルダをまるごと削除してしまったとか、プロジェクト名やバージョンを変更した場合、ファイルを探しに行く場所にはファイルは存在しません。そこで例外処理をおこなっています。ピッチャーが暴投しても優秀なキャッチャーがいれば例外は怖くありません。

これで前回終了時に存在した鳩を起動時に復元することができるようになりました。