こんにちは、皆さん。今日は、本格的なアプリケーションを、作ってみましょう。概要だけでは、中々使い方も分からないし、「目標」も定まらないと思います。
どんな使い方が出来るのかという、一つの参考にして貰えれば良いと思います。
とは言え、まぁ本格的とはいっても、まだまだ難しいとまでは行きません。しかし、自分で使うものとしては、これくらいの方が、実用的かも知れません(笑)。
また、非常に「基本的」で「必要不可欠」なことが、ふんだんに含まれているので、是非この機会に習得してみましょう。
今回作るのは、自分専用の「置時計」です。何の変哲もない、家にあるような「アナログ時計」です。特に、装飾もしていませんし、「付加機能」も付けていません。
それらは各個人で、独自に追加・実装してください。
それでは、早速、見て行きましょう。可なり難しい部分もあるので、焦らずにゆっくりと進みましょう。Let’s get started !
「MyTableClock プロジェクト」を作成します。
先ず、「Visual Studio Community」を起動して、「Windows フォーム アプリケーション (.NET Framework)」を作成します(通常)。
この時の「プロジェクト名」を、「MyTableClock」にします。
作成に関する詳しいことは、以下の記事を参照してください。

次に、基本のフォーム(Form)を、作成しましょう。
前回の記事、「TabControl コントロールの使い方について。」で、詳しく説明していますが、「BaseTabForm.cs(本体の Form)」を、コピー&ペーストしましょう。
当サイトで扱うアプリケーションは、殆どがこの形式なので、是非習得しておきましょう。一度作れば、何度でも再利用できるので、効率的です。
詳しくは、以下の記事を参照してください。

尚、当サイトの記事は、初心者の方が途中から見ても、中々分らない事が多いと思います。
ですから、トップページにある「オススメ記事」を、最初から確りと、身に付けておきましょう。難しい事は何もありませんが、「 空 」で出来るようになるには、大変かも知れません。
考える必要はありません。只管愚直に行う方が、良いかも知れません。というのは、頭で覚えるよりも、体で覚えた方が実用的だと思います。
意味は、後から徐々に分かってきます。まぁ、言葉は悪いのですが、「下手の考え休むに似たり」という諺もあります(汗)。
逆に、スキルのある方は、途中からでも「十分」理解できると思います。
そして、基本のフォーム「BaseTabForm.cs」をコピー&ペーストして、その名前を「TableClockForm.cs」に変更します。
ここ迄を、完全に実行しましょう。ここ迄が上手く行かなければ、その後も上手く行きませんので。
もし失敗したのなら、一からやり直しましょう。難しいことではなく、簡単な操作なので時間は掛からないと思います。
「諺」の付箋
まぁ今日は、男の本懐についての諺を、一つ紹介します(大袈裟)。(^_^;)
それは、「男は敷居を跨げば七人の敵あり」、という諺です。
意味は、男は社会に出て仕事をすると、どんな人でも七人の敵がいるということです。これは、本当にそうですよね。
どんなに良い事をしても、そうなります(笑)。まぁしかし、敵ばかりではなく、称賛してくれる人や、助けてくれる人も沢山います(good)。
そして、まぁ処世術としては、前回の記事にも書きましたが、「批判」しかない所に、「発展」もないですからね、「本質」や「良い所」を見て行きましょう!。
ファイトと、Take it easy ! ですよ。先ずは、自分で見聞きした感性を、信じましょう。
それでは、先ず「完成図」を見ておきましょう。
今回は、この「アナログ時計」を、一から作ります。この事は、単に「アプリケーションを一つ作る」、という事ではありません。
このアプリケーションを通じて、「グラフィックスの仕組み」や「グラフィックスの移動」、そして「タイマー(Timer)クラスの使い方」を、習得するという試みです。
特に、グラフィックスに関しては、色んな方法がありますが、今回の手法もその一つであり、大小に関わらずアプリケーションの基本になります(一応)。
ですから今回は、簡単ではありますが、後で確りと、意味を理解しておきましょう。
それでは、以下が完成図です。
この「置時計」を、これから作成します。先ずは、「確認事項」からです。

次に、コピー&ペーストした、フォームを確認します。
先ず、以下の状態になっているかを、確認してください。一応、デスクトップを基本としているので、ノートパソコンの場合は、それなりに工夫してください。m(_ _)m


確認する部分は、
- ソリューションエクスプローラーで、「TableClockForm.cs」になっているか?
- 名前空間(namespace)が、「MyTableClock」になっているか?
- エラーは、出ていないか?
という事です。「置時計のデザイン」は、この状態から始めます。初心者の方は、ここ迄が出来ていないと、先には進めないと思います(多分、意味的に)。
逆に、スキルのある方は、意味が分かっているので、何処からでも進めると思います。
フォームのデザインを、置時計用に変更します。
置時計の場合は、少しフォームを小さくして、大きさが変更出来ないようにします。それに伴って、不要なコントロールとイベントを削除します。
ところが、削除する場合は、簡単だと思うかも知れませんが、「削除する順番」を間違えると、初心者の方にとっては、クリティカル(致命的)なエラーになる場合があります。
というのは、イベントが絡んでいるので、デザイナー画面に進めない、という事になります。勿論、スキルのある場合には、簡単にエラーを修正出来るのですが(笑)。
基本的に、イベントが絡んでいる場合は、先ずそのコントロールから削除します。その後で、ソースコードを削除します(飽くまでも、基本です)。
それでは、次の「順番通り」に、確実に進めてください。
正確には、「ToolStripStatusLabel コントロール」の一部を削除します。
今回は、フォームの大きさを小さくして、大きさを変更出来ないようにするので、右下にある「ペインの開閉」は、不要になります。それを削除します。
- フォームの「デザイン画面」で、一番下にある「StatusStrip」をクリックします。
- 右側のプロパティグリッドで、「Items: (コレクション)」の三点リーダーをクリックします。
- 「項目コレクションエディター」が現れるので、六個の「A StatusLabel」の内、下から三個を削除(×)します。
- 残すのは、上から「toolStripStatusLabel3」までです。
- 右下の「OK」をクリックします。
今回は、本当に小さなアプリケーションなので、「TabPage コントロール(SideTabControl)」は、2ページで充分です。
- フォームの「デザイン画面」で、右側(ペイン)の「TabControl」をクリックします。右上の「小さな三角形」付近をクリックすると、選択出来ます。
- 右側のプロパティグリッドで、中程にある「TabPages: (コレクション)」の三点リーダーをクリックします。
- 「TabPage コレクションエディター」が現れるので、「tabPage3」を削除します。
- 右下の「OK」をクリックします。
- 次に、「tabPage1」 をクリック(右ペインの真ん中辺り)して、プロパティグリッドの表示を変更します。
- そしてプロパティを、「(Name) : AddTabPage」、「Text: 付加機能」に変更します。
フォーム(Form)の一番上をクリックして、Form のプロパティを表示します。そして、以下のように変更します。変更部分だけです。
| MaximizeBox | False | 最大化ボタン |
|---|---|---|
| (Name) | TableClockForm | 名前(既) |
| Size | 650, 570 | サイズ |
| FormBorderStyle | FixedSingle | 境界線スタイル |
| Text | MyTableClock | テキスト |
今回の場合は、「DoubleBuffered プロパティ」は、どちらでも良いです。
これを、デザイナー上で選択するのは、結構難しいのですが(笑)、「Panel1」の右側にある境界線(8 pixel)をクリックすると、選択できます。
確実に選択する場合は、右側のプロパティグリッドの上部に、「コンボボックス」があるので、そこから「太字の SplitContainer」を、選択します。
そして、プロパティの「SplitterDistance」を、「370」に変更します。
ソースコードから、イベントを削除します。
「ペインの開閉」のイベントは、今回は必要ないので、削除します。そして、初期設定を、少しだけ変更します。それでは、「ソースコード」のタブをクリックします。
先ずは、「#region ● イベント ~ #endregion」の中にある、
「private void PaneStatusLabel_Click(object sender, EventArgs e) {……}」を、コメントも含めて、バッサリと削除します。
そして、「● 初期設定」の部分を、
SplitContainer.SplitterDistance = 370;
MessageStatusLabel.Text = “大きさは、変更出来ません。”;
に変更します。以上です。これで、「置時計」用のフォームに成ったので、動かしてみましょう。「▲ 開始」をクリックするか、または「F5」を押下しましょう。
以下のように成れば、大成功です。この状態から、「置時計」を作成します。ここ迄を、「 空 」で出来るようになれば、基本は卒業です(笑)。

「冗談(By way of a joke)」で一息。
「馬の耳に念仏」に、成らないようにしましょう(笑)。それには、何かに「興味」を持つことが、重要でしょうか?。
それによって、「進むべき道」というものが出来るからです。「全ての道はローマに通ず」という格言もあります。
或いは、「鶏口となるも牛後となるなかれ」、ですかね。ワンランク落とすと、楽になりますからね!(オススメ)。
まぁ、とは言っても、向上心も重要です(笑)。
それでは、コントロールを追加して行きます。
今回は、「アナログ時計」を作成するので、重要なのは「PictureBox コントロール」になります。グラフィックスを描画する場合は、殆どこのコントロールを使用します。
勿論、「Form コントロール」に、直接描画することも出来ますし、汎用的で「出来る事」も「付加価値」も高いのですが、再利用という点からすると、難しいです。
ですから、特別な機能を必要としない場合は、「PictureBox コントロール」で充分です。それでは、左側の「Panel1」から、デザインをして行きましょう。
その前に、プロパティグリッドで、「SplitContainer.Panel1」の「BackColor」を、「White(白)」にしておきましょう(重要)。
「ツールボックス」から「Label」をクリックして、ドラッグ&ドロップで SplitContainer の 「Panel1」上に貼ります(場所は、どこでも構いません)。
そのまま、以下のプロパティを設定します。
| (Name) | DateLabel | 名前 |
|---|---|---|
| AutoSize | False | 自動サイズ |
| Location | 20, 20 | 位置 |
| Size | 320, 30 | サイズ |
| Font | Yu Gothic UI, 12pt | フォント |
| TextAlign | MiddleCenter | テキストの位置 |
「ツールボックス」から「PictureBox」をクリックして、ドラッグ&ドロップで「Panel1」上に貼ります(場所は、どこでも構いません)。
そのまま、以下のプロパティを設定します。
| (Name) | ClockPictureBox | 名前 |
|---|---|---|
| Location | 20, 60 | 位置 |
| Size | 320, 320 | サイズ |
| BackColor | White | 背景色 |
後で、「Paint イベント」を設定します。
「ツールボックス」から「Label」をクリックして、ドラッグ&ドロップで「Panel1」上に貼ります(場所は、どこでも構いません)。
そのまま、以下のプロパティを設定します。
| (Name) | TimeLabel | 名前 |
|---|---|---|
| AutoSize | False | 自動サイズ |
| Location | 20, 400 | 位置 |
| Size | 320, 30 | サイズ |
| Font | Yu Gothic UI, 12pt | フォント |
| TextAlign | MiddleCenter | テキストの位置 |
「AddTabPage」に、付加機能を付けます。
一応、「時計」なので、「付加機能」を付けておくと、便利に使えると思います。
とは言っても当サイトでは、「何が便利なのか?」は、各個人によって異なるので付けません。それは、今後各個人で「自由に」実装してください。
ただし、「音を鳴らす」にしても結構難しいので、スキルが上がってからにしましょう(笑)。
当サイトでは、一つだけ「Form の便利機能」を、紹介しておきます。実は、「Form」には、通常のコントロールには出来ない「機能」が、含まれています。
それらを使うと、本格的な「置時計」も、夢ではありません(笑)。今回紹介するのは、その一つです。非常に簡単なのですが、実用的かも知れません。
「ツールボックス」から「Label」をクリックして、ドラッグ&ドロップで「AddTabPage」上に貼ります(場所は、どこでも構いません)。
そのまま、以下のプロパティを設定します。
| Location | 20, 330 | 位置 |
|---|---|---|
| Size | 144, 20 | サイズ(AutoSize) |
| Text | 不透明度( 50 – 100 ): | テキスト |
| TextAlign | MiddleLeft | テキストの位置 |
「ツールボックス」から「Label」をクリックして、ドラッグ&ドロップで「AddTabPage」上に貼ります(場所は、どこでも構いません)。
そのまま、以下のプロパティを設定します。
| (Name) | OpacityLabel | 名前 |
|---|---|---|
| Location | 170, 330 | 位置 |
| Size | 45, 20 | サイズ(AutoSize) |
| Text | 100% | テキスト |
| TextAlign | MiddleCenter | テキストの位置 |
「ツールボックス」から「TrackBar」をクリックして、ドラッグ&ドロップで「AddTabPage」上に貼ります(場所は、どこでも構いません)。
そのまま、以下のプロパティを設定します。
| (Name) | OpacityTrackBar | 名前 |
|---|---|---|
| LargeChange | 10 | 大きい変化量 |
| Maximum | 100 | 最大量 |
| Minimum | 50 | 最小量 |
| Value | 100 | 現在の量 |
| Location | 24, 370 | 位置 |
| Size | 200, 56 | サイズ(AutoSize) |
| TickFrequency | 10 | 目盛りの量 |
これは、「TrackBar コントロール」を使って、「Form の不透明度」を、「50% ~ 100%」まで変化させます。
注意することは、「0%」にすると「完全に消える」ので、捕捉する手段がありません(激汗)。ですから、「50%」に留めています。
最も重要な、「Timer コントロール」を貼ります。
今回は、この「Timer コントロール」を使って、日時(日付、時分秒)を刻みます。
「C#」には、「時間」や「時刻」に関するクラスは、他にも幾つか有りますが、最も簡単で手軽に使えるという意味では、今回の使用に適しています。
それでは、 「ツールボックス」から「Timer」をクリックして、ドラッグ&ドロップで「Panel1」上に貼ります(場所は、どこでも構いません)。
一番下の欄に、表示されます。そして、プロパティグリッドで、「(Name): ClockTimer」、「Interval: 1000」にします。
以上で、デザインは、ほぼ終了です。後は、「イベント」を四つ作成すれば、終わりです。
イベントを四つ作成します。
それでは、「デザイン」タブから、イベントを作成します。この「イベントの設定」を、「デザイン」タブから、自由自在に作成出来るようになれば、「基本的」なことは卒業です。
しかし、本来は、「ソースコード」から「イベント」を、設定出来るようになれば、「イベントを完全に理解できた」という事になります。
尚、「デザイン」タブから、「イベントを設定」するという事は、ソースコード上に、イベントを処理する「空のメソッド」を、作成するという事です。
デザイン画面の下にある、「ClockTimer」をクリックします。プロパティグリッドの上部にある、「雷(§)のアイコン」をクリックします。
「Tick」を、ダブルクリックします。これで、ソースコードに「ClockTimer_Tick イベント」が、作成されました。
デザイン画面に戻って、左ペインにある「ClockPictureBox」をクリックします。プロパティグリッドの上部にある、「雷(§)のアイコン」をクリックします。
この場合は、既になっています。
イベント項目の一番下にある「Paint」を、ダブルクリックします。これで、ソースコードに「ClockPictureBox_Paint イベント」が、作成されました。
デザイン画面に戻って、右ペインにある「OpacityTrackBar」をクリックします。プロパティグリッドの上部にある、「雷(§)のアイコン」をクリックします。
イベント項目の上部にある「ValueChanged」を、ダブルクリックします。これで、ソースコードに「OpacityTrackBar_ValueChanged イベント」が、作成されました。
デザイン画面に戻って、「フォーム(Form)」の一番上をクリックします。プロパティグリッドの上部にある、「雷(§)のアイコン」をクリックします。
イベント項目の下の方にある「FormClosed」を、ダブルクリックします。これで、ソースコードに「TableClockForm_FormClosed イベント」が、作成されました。
これは、独自に設定した「リソース」を、開放するために行います。
以上です。これで、全ての「デザイン」が終わりました。ご苦労様でした。m(_ _)m
尚、「雷(§)のアイコン」の左側にある「アイコン」をクリックすると、「プロパティの選択」に戻ります。
諺の付箋。
「旅の恥は掻き捨て」、という諺があります。これは、旅に出ると顔見知りも居ないので、普段なら出来ない事でもしてしまう、という意味です。
これは、良い意味にも、悪い意味にも使えます。良い意味としては、「大胆」になれるという事です。悪い意味としては、色々ありますね(汗)。
しかし、基本的には、「良い意味」で使うことが多いですね。せっかく旅に来たのだから、遠慮せずに楽しもう!。ということだと思います。
それともう一つ、重要な諺があります。それは、
「聞くは一時の恥、聞かぬは一生の恥」、という諺です。
これは、「世間体」を気にして、「本質」を見逃すという事でしょうか?!。自分にもこういう所があるので、気を付けたいと思います。
次は最も難しい、「ソースコード」の作成ですが、・・・
「ソースコード」というのは、実は、これが「正解」というものはありません。100人のプログラマーが居れば、100通りの「ソースコード」があって当たり前です。個性ですかね。
ただし多分、結果は全て同じでしょうが(笑)。
ですから、今回紹介するのは、飽くまでも「私が信じている基本」です。オススメはしていますが、何の保証もしていません(汗)。
まぁ通常は、一通り自分で作ってから、「以下のソースコードを見て下さい」という流れなんですが、今回は違います。
何故なら、初心者の方が、「完成されたデザイン」だけで、ソースコードを作成することは、「多分、殆ど不可能」だと思うからです。
勿論、マニュアルを駆使して、ネット検索を駆使して、AIを活用すれば、出来るかも知れませんが、それはそれなりに難しいと思います(笑)。
ですから、今回は、そのまま「見て覚えて下さい」。先ずは、手入力で「コメント」も含めて、「ソースコード」を打ち込みましょう。
まぁ、プロの方が、「動作確認」をする為に、「コピペ」するのは構いませんが、初心者の方が、「身に付く方法」ではありません。
最も良い習得方法は、ソースコードを打ち込みながら、「疑問点」をコメント(//)にしておく事です。そうしておくと、後で「疑問点」を解決し易くなります。
また、コメントに「TODO: 又は、TODO」を先頭に付けると、「タスク一覧:表示(V)の中にあります」に、表示されます。
逆に、プロの方は、「ノールック」で作成してから、「コンペアルック」をすると、面白いと思います。
尚、今回の「ソースコード」は、「難しい」という程ではありませんが、「簡単」という程でもありません。数学の「回転」という概念が、必要になります。
或いは、「数学の座標系」と「C#の座標系」は、「Y軸」の方向が異なるので、それなりに考慮が必要になります。
ですから、「ソースコード」の説明はしませんが、作成に必要な「概念」を、次に説明したいと思います。尚、「ソースコード」には、ある程度「説明」を入れています。
「置時計」を作る場合に、必要な概念。
先ず、グラフィックスを描画する場合に、最も重要な事は、「数学の座標系」と「C#の座標系」が、異なっているという事です。
ですから、この事に慣れることが重要です。何故なら、計算や概念は全て、「数学の公式」をそのまま使います。しかし、その値が、画面上に表示される場合は、数学上とは異なります。
それは、「Y軸」の方向が、異なっているからです。それに伴って、角度(θ)の方向も異なります。
例えば、( X, Y ) = ( 0, 0 )、( 100, 100 )、( 200, 200 )、・・・、( 500, 500 ) の点を描画する場合、数学上では、「右( X+ )上( Y+ )」に伸びて行きます。
しかし、C#の画面上では、「右( X+ )下( Y+ )」に伸びて行きます。回転の場合も、それぞれ「Y軸」の(+)方向に回転して行きます。
C#の描画に関しては、主に「Graphics オブジェクト」が担当していますが、そう成っています。ですから、これ等に慣れることが重要になります。
ただし、頭で覚える「事」や「必要」は、何もありません。「概念」として、「あッ、そう成っているんだ!」という事を、理解して置きましょう。
というのは、頭で覚えていても、直ぐに忘れます(爆笑)。また、「プログラム言語」毎に、「状況」が異なる場合もあります。
ですからそれよりも、プログラムを組む直前に、小さなプログラムを作って、「特性」や「特徴」を確認しましょう。
例えば、以下のようなプログラムを、「PictureBox 等の Paint イベント」で走らせて確認します。
e.Graphics.DrawRectangle(Pens.Red, new Rectangle(0, 0, 250, 250));
e.Graphics.DrawArc(Pens.Green, new Rectangle(0, 0, 250, 250), 0, 300);
これは、どんな言語においても、多分そんな感じです。
「三角関数」の概念が、必要になります。
まぁ、日常において、「三角関数」を使うことは、殆ど無いかも知れません。しかし、知らず知らずの内に、「三角関数」を使っている場合があります。
例えば、「車のハンドル」や「船の舵」ですよね。あれは「円」ですから、角度(θ)が重要になります。或いは、これから作る「置時計」ですね。これも、「円」です。
或いは、「星」を描く場合に、「分度器」を使いますよね(72 度毎に)。
しかし、もう少し顕著なのが、仕事となると結構「三角関数」は、直ぐに必要になります。「三角関数」というのは、「高校数学IA」で習得します。
ですから、原理さえ確りと理解しておけば、決して難しい事ではありません。今回、使用するのも、本当に簡単な「公式」だけです。この機会に、是非、習得しておきましょう。
実は、「C#」の「Graphics オブジェクト」には、非常に実用的な機能が、豊富に揃っています。描画を美しく見せるメソッドや、「移動・回転」に関するメソッドです。
これらは、独自に実装しようとすると、非常に難しいのですが、C#の場合は、たった一行で実現出来たりします。
今回使うものは、以下の通りです。
- 「移動」に関する、「e.Graphics.TranslateTransform(原点にしたいX、Y座標)」。
- 「回転」に関する、「e.Graphics.RotateTransform(回転角:Degree)」。
です。特に回転は、「rad: ラジアン」ではなく、「deg: 度(ディグリー)」で扱えるので、便利です。
これらの変換は、「対象物が、移動・回転する」というよりも、「座標系が、移動・回転する」と考えた方が、考え易いかも知れません。
この場合、プログラマーは、難しい「数学計算」が必要ないので、非常に楽です。
これは、主に、「文字の描画」ですね。まぁ、文字自体を「回転」させたい場合は良いのですが、逆に「回転」させたくない場合があります。今回のように。
この時は、「数学の三角関数」を使って、表示位置を計算するしかありません!?(多分)。
とは言っても、非常に簡単です。次の「二つの公式」から、表示位置を計算します。
- X座標は、= 中心のX座標 + 半径 × COS(角度:ラジアン)
- Y座標は、= 中心のY座標 + 半径 × SIN(角度:ラジアン)
これだけです。これを使って、文字を正確に描画します。

一応、「数学上の概念」を、表示しておきます。
これが、「三角関数」の基本です。
描画する場合は、常に上書きですから、後から描画したものが上になります。
ですから、「C#の移動・回転」を使う場合と、使わない場合に対して、一旦「Graphics オブジェクト」を、元に戻す必要があります(Reset)。
或いは、必要な「状況」に戻すことになります(Restore)。この辺りが、難しい所です。
この事は、一見、当たり前のように思うかも知れませんが、重要なことが含まれています。
それは、「Paint イベント」で描画する範囲は、「その1秒に起きる事」だけで充分だという事です。それ以外のことは、何もする必要がありません。
つまり、過去の描画を、消したりする必要がないという事です。これは、本当に良い事なんです(笑)。
尚、「Paint イベント」は、この「Tick イベント」から呼び出します。
まぁ、この辺りを突き詰めると、「最適化」という事になりますが、スキルが上がってから、考えましょう(笑)。
本来なら、最もスマートな「ソースコード」を提供するのが、世の常だと思うのですが、実は、最もスマートな「ソースコード(最適化)」は、将来自分で実装してください。
何故なら、それは、当サイトの目的では無いからです。
今回は、「Graphics オブジェクト」の「移動・回転・Reset・Restore」を焦点に、ソースコードを作成しています。
「マネージドコード」を扱う場合は、「ガベージコレクション」にお任せで大丈夫なんですが、「グラフィックス」等を扱う場合は、そうは行きません。
というのは、描画に関する「リソース」は、Windows で提供されている「GDI/GDI+」を、使用しています(WPF は、DirectX)。
これ等は、「アンマネージド・リソース」を含んでいるので、「ガベージコレクション」の対応だけでは、間に合いません。
ですから、「グラフィックス」や「大きなメモリー」を扱う場合は、プログラマーが明示的に、リソースを開放する必要があります。
今回も、それに準じています。
それでは、ソースコードを公開します。
例によって、一応、「アコーディオン」にしています(笑)。非常に長いので、覚悟しておいてください!。習得したい場合は、「手入力」で打ち込みましょう。
その為にも、「良いキーボード」は、必須ですか?!。私の感覚としては、「打鍵感」がある方が、良いですね(人其々ですが)。
私自身は、日常は、結構安い物を使っています。高級なものも持っていますし、以前は使っていました(笑)。
「ソースコード」は、常に、実際に動いているものを、提供しています。ですから、もし、動かない場合は、「デザインの作成」の方を、確認して見てください。
TableClockForm のソースコードです。
using Microsoft.SqlServer.Server;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.DataFormats;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ProgressBar;
namespace MyTableClock
{
/// <summary>
/// 自分用の置き時計を提供します。
/// </summary>
public partial class TableClockForm : Form
{
/// <summary>
/// 新しいインスタンスを作成します。
/// </summary>
public TableClockForm()
{
// デザイナーが行う初期設定です。
InitializeComponent();
// 個人的な初期設定を行います。
InitializeMyMember();
}
/// <summary>
/// 終了処理を行います。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TableClockForm_FormClosed(object sender, FormClosedEventArgs e)
{
// 独自に設定したリソースを開放します。
ClockFont.Dispose();
}
#region ● 初期設定
/// <summary>
/// 初期設定を行います。
/// </summary>
private void InitializeMyMember()
{
// 正確に値を設定したい場合は、コードで設定します。
SplitContainer.SplitterDistance = 370;
// 時計関係を初期設定します。
// (デザインからでも設定できますが、白と白で見えなくなるので。)
DateLabel.BackColor = Color.White;
TimeLabel.BackColor = Color.White;
// フォントを設定します。
ClockFont = new Font(this.Font.FontFamily, 14);
// 中心位置を設定します。
ClockCenterPt = new PointF(
ClockPictureBox.Width / 2f, ClockPictureBox.Height / 2f);
// 時刻の初期表示を行います。
// (空で呼び出すことは、良くあります。)
ClockTimer_Tick(null, null);
// 最初のご挨拶。
MessageStatusLabel.Text = "大きさは、変更出来ません。";
}
#endregion
#region ● プロパティ
/// <summary>
/// 度をラジアン変換する時の値。
/// </summary>
/// <remarks>
/// Radian(ラジアン)は、「C#」では必須なので、習得しましょう。
/// </remarks>
private const double Radian = Math.PI / 180.0;
/// <summary>
/// 時計のフォントを取得または設定します。
/// </summary>
private Font ClockFont { get; set; }
/// <summary>
/// 時計の中心位置を取得または設定します。
/// </summary>
private PointF ClockCenterPt { get; set; }
/// <summary>
/// 現在の日時を取得または設定します。
/// </summary>
private DateTime CurrentDateTime { get; set; }
/// <summary>
/// 一つ前の日時を取得または設定します。
/// </summary>
private DateTime LastDateTime { get; set; }
#endregion
#region ● イベント
/// <summary>
/// Form の不透明度を変更します。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OpacityTrackBar_ValueChanged(object sender, EventArgs e)
{
// 小数点を付けると、倍精度になります。
this.Opacity = OpacityTrackBar.Value / 100.0;
OpacityLabel.Text = this.Opacity.ToString("##%");
}
/// <summary>
/// 1秒毎にタイマーが発生します。
/// </summary>
/// <remarks>
/// 起動した時点から、1秒毎に発生します。
/// </remarks>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClockTimer_Tick(object sender, EventArgs e)
{
// 現在日時を取得します。
DateTime dt = DateTime.Now;
CurrentDateTime = dt;
if (CurrentDateTime.Date != LastDateTime.Date)
{
// 年月日の表示には、ToLongDateString() を使えますが、
// もう少し見易くする為に、自前で表示します。
DateLabel.Text = String.Format(
"{0:####} 年 {1:##} 月 {2:##} 日 ({3:ddd})",
dt.Year, dt.Month, dt.Day, dt);
}
// 時刻も自前で表示します。
TimeLabel.Text = String.Format(
"{0} : {1:00} : {2:00}",
dt.Hour, dt.Minute, dt.Second);
// 時計を表示します。
// (Paint イベントを、呼び出す方法です。)
ClockPictureBox.Invalidate();
// 日時を保存します。
LastDateTime = CurrentDateTime;
}
/// <summary>
/// 時計を表示(描画)します。
/// </summary>
/// <remarks>
/// 描画における、最も重要なイベントです。
/// Graphics オブジェクトの移動・回転を学びましょう。
/// </remarks>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClockPictureBox_Paint(object sender, PaintEventArgs e)
{
// グラフィックの表示を高品質にします。
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
// 時計版の目盛りを表示します(回転を使います)。
DisplayClockMarkings(e);
// 時計版の文字を表示します(数学計算を使います)。
DisplayClockDial(e);
// 時計の回転位置を、時計の中心に設定します。
e.Graphics.TranslateTransform(
ClockCenterPt.X, ClockCenterPt.Y);
// この状態を保存します。
GraphicsState transState = e.Graphics.Save();
// 時針を表示します(回転を使います)。
DisplayClockHour(e);
// 変換を保存状態に戻します。
e.Graphics.Restore(transState);
transState = e.Graphics.Save();
// 分針を表示します(回転を使います)。
DisplayClockMinute(e);
// 変換を保存状態に戻します。
e.Graphics.Restore(transState);
// 秒針を表示します(秒の分だけ回転させます)。
e.Graphics.RotateTransform(
6f * CurrentDateTime.Second);
// 描画します。
e.Graphics.DrawLine(Pens.Red,
PointF.Empty, new PointF(0, -114));
// 変換をリセットします。
e.Graphics.ResetTransform();
}
/// <summary>
/// 時計版の目盛りを表示します。
/// </summary>
/// <remarks>
/// Graphics の移動・回転を使用します。
/// </remarks>
/// <param name="e"></param>
private void DisplayClockMarkings(PaintEventArgs e)
{
// 回転位置を、時計の中心に設定します。
e.Graphics.TranslateTransform(
ClockCenterPt.X, ClockCenterPt.Y);
// using() は、リソースを自動的に開放します。
using (var sPen = new Pen(Color.Orange, 2))
{
// 表示位置を設定します。
PointF sprPt = new PointF(0, -114);
PointF norPt = new PointF(0, -120);
PointF endPt = new PointF(0, -130);
// 60個の目盛りを表示します。
for (int i = 1; i <= 60; i++)
{
// 6度回転させます。
e.Graphics.RotateTransform(6f);
// 5分毎の場合(% は、剰余)。
if (i % 5 == 0)
{
e.Graphics.DrawLine(sPen, sprPt, endPt);
}
// それ以外の場合。
else
{
e.Graphics.DrawLine(Pens.Black, norPt, endPt);
}
}
}
// 変換をリセットします。
e.Graphics.ResetTransform();
}
/// <summary>
/// 時計版の文字を表示します。
/// </summary>
/// <remarks>
/// 回転を使うと文字自体が回転するので、数学計算を行います。
/// </remarks>
/// <param name="e"></param>
private void DisplayClockDial(PaintEventArgs e)
{
// 文字の表示を、上下左右の中央に設定します。
StringFormat cdFormat = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
// 時計回りに、1 から 12 まで表示します。
for (int i = 1; i <= 12; i++)
{
// 角度をラジアンに変換します。
double cdAng = (30.0 * i - 90.0) * Radian;
// 表示位置を計算します(三角関数の基本です)。
PointF cdPt = new PointF(
ClockCenterPt.X + (float)(145.0 * Math.Cos(cdAng)),
ClockCenterPt.Y + (float)(145.0 * Math.Sin(cdAng)));
// 文字を表示します。
e.Graphics.DrawString(i.ToString(), ClockFont,
Brushes.RoyalBlue, cdPt, cdFormat);
}
// 後処理をします。
cdFormat.Dispose();
}
/// <summary>
/// 時針を表示します。
/// </summary>
/// <remarks>
/// Graphics の回転を使用します。
/// </remarks>
/// <param name="e"></param>
private void DisplayClockHour(PaintEventArgs e)
{
// ペンを作成します。
// (ペンが不変の時は、再利用する場合もあります。)
Pen hPen = new Pen(Color.Gray, 6)
{
Alignment = PenAlignment.Center,
StartCap = LineCap.Flat,
EndCap = LineCap.Triangle
};
// 時針を回転させます。
// (時針が、時分秒で何度動いたかを計算します。)
e.Graphics.RotateTransform(
30f * CurrentDateTime.Hour +
0.5f * CurrentDateTime.Minute +
0.5f * CurrentDateTime.Second / 60f);
// 描画します。
e.Graphics.DrawLine(hPen,
PointF.Empty, new PointF(0, -84));
// 使用したペンは、破棄します。
hPen.Dispose();
}
/// <summary>
/// 分針を表示します。
/// </summary>
/// <remarks>
/// Graphics の回転を使用します。
/// </remarks>
/// <param name="e"></param>
private void DisplayClockMinute(PaintEventArgs e)
{
// ペンを作成します。
Pen mPen = new Pen(Color.Gray, 4)
{
Alignment = PenAlignment.Center,
StartCap = LineCap.Flat,
EndCap = LineCap.DiamondAnchor
};
// 分針を回転させます。
// (分針が、分秒で何度動いたかを計算します。)
e.Graphics.RotateTransform(
6f * CurrentDateTime.Minute +
0.1f * CurrentDateTime.Second);
// 描画します。
e.Graphics.DrawLine(mPen,
PointF.Empty, new PointF(0, -104));
mPen.Dispose();
}
#endregion
}
}
結構難しい、「精度」や「付加機能」について。
今回は、特に「精度」に関しては、殆ど気にしていませんが、もし正確に時を刻みたい場合は、少し工夫が必要になります。
というのは、このアプリケーションの「1秒」は、起動した時点からの「1秒刻み」です。ですから、「Windows の時刻」と、同期している分けではありません。
従って、最大で「1秒以内」の誤差が、発生します。この誤差を解消しようとすると、可なり難しいと思います。さらに、「最適化」等も必要になって来ます。
ですから、スキルが上がってから、ゆっくりと解決しましょう。
それと、「付加機能」として、「ストップウォッチ機能」を実装した、プロトタイプを作成したのですが、一応動くことは動くのですが、「UI」が非常に複雑になります。
また、「Timer クラス」を2つ使う事になりますし、「表示」も専用にカスタマイズするのが、難しくなります。
ですから、初心者の方には、お勧めしません。「ストップウォッチ機能」は、別のアプリケーションとして、「単独」で作った方がより「実用的」です。
最後に、「最適化」について。
描画を行う場合に「最も重要」なのが、「速度」です(私の独断ですが)。しかし、この「速度」という概念には、色んな方法があります。
今回は、「力業」ではなく、「知恵を絞って」考えてみましょう。そういう意味で、思い付くままを列記してみます。
それは、「描画する数」を減らすことです。或いは、「計算する数」を減らすことです。もう少しいうと、「変化」する部分と「不変」の部分を、洗い出してみる事です。
そうすると、「変化」する部分は、「時分秒の針」です。「不変」の部分は、時計盤の「目盛り」と「文字」です。
ですから、時計盤の「目盛り」と「文字」を、「ビットマップ画像」に一度だけ作成して、それを「PictureBox.Image プロパティ」に設定すれば、良さそうですね。
そうすると、「Paint イベント」では、「時分秒の針」だけを、描画すれば良い事になります。「時計盤」は、PictureBox が自動的に表示してくれます。
恐らくこの方法が、最も「効率的」で「速い」、描画方法だと思います(多分)。
実際に、初期設定で「時計盤」を作成して、「PictureBox.Image プロパティ」に設定してみましたが、体感できる速度の違いは、分かりませんでした。(^_^;)
まぁしかし、非常に凝った「時計盤」や、「高精細」なものを作る場合は、この方法が「必須」だと思います。
今回のような「小さな物」の場合は、そこまで考える必要はないと思います。
実は、描画の過程において「コスト(時間)の掛かるリソース」は、再利用する方が高速になります。
ですから、「Paint イベント」で「作成・破棄」するよりも、「アプリケーションレベル」で「作成・破棄」する方が、良いと考えますよね、普通は。
ところが、今回のような小さなアプリケーションでは、また、描画頻度が「1秒毎」の場合は、そこまで考える必要はありません。
何故なら、グラフィックスにおける基本は、「使い捨て」だからです(一応)。
今回のように、自分で使う「小さなアプリケーション」では、実は、「最適化」を行っても、「五十歩百歩」だと思います。
というのは、「PictureBox.Image プロパティ」に設定したとしても、「高速」という意味にはなりますが、何もしていない分けではありません。
或いは、「リソース」にしても、無限大に、使える分けではありません。その辺りのことを、考慮しておきましょう。
そうですね、最適化が必要な場合は、デスクトップの広い画面一杯に、一秒間に十数回以上描画する、「ゲーム」とか「CAD」ですかね?。(・_・;)
まぁしかし、これらは「DirectX」とか、「OpenGL」の世界なので、当サイトで、解説できる範囲ではありません。m(_ _)m
まぁ、兎に角、私から言えることは、「余り考え過ぎない」ことですね。でないと、「最適化」のことばかりが気になって、「本来の良さ(本質)」が失われます。
まとめ。
如何でしたでしょうか、上手く動きましたか?!。一度や二度では、中々理解もできないし、身に付かないと思います。
まぁしかし、色々と失敗を重ねたり、付加価値を付けることによって、少しずつ進歩して行くと思います。頑張ってみて下さい。
今後も、「コーヒーブレイク」として、「ことわざ」や「格言」や「偉人の名言」等を、入れて行こうと思います。
それでは、この辺でごきげんよう。(^_^)/


コメント