Windows GUIプログラミング入門2 デジタル時計

# ■はじめに
今回はデジタル時計を作ります。
ウィンドウサイズに合わせて時刻の文字サイズも変わります。

完成図
![wbg2-26.png](https://qiita-image-store.s3.amazonaws.com/0/142879/75b43d26-231f-7690-6ef5-bea4450d15fb.png)

キーワード：ウィンドウリサイズ, タスクバー表示, 最前面表示, Viewbox, DispatcherTimer, タイマー, ドキュメントコメント, イベント, using整理

[注意]
これまでの回で説明済みの操作方法等は、説明を省略したり簡略化している場合があります。

# ■開発環境
* Windows 10
* Visual Studio Community 2015 Update 3
* .NET Framework 4.x

# ■作ってみる
新規プロジェクトでWPFアプリケーションを選択します。
プロジェクト名は`WpfApplication2`にします。

## ◇ウィンドウの高さを変える
画面デザイナのウィンドウのタイトルバーの部分をクリックしてください。
そうするとXAMLの`Window`タグの部分が選択状態になり、プロパティウィンドウの「種類」が`Window`になります。
![wbg2-01.png](https://qiita-image-store.s3.amazonaws.com/0/142879/8f98a158-7284-d790-598b-563bd6984969.png)

プロパティの検索ボックスに `hei` と入力してください。
プロパティの一覧がheiを含むものにフィルタリングされて、`Height`が出てきます。
![wbg2-02.png](https://qiita-image-store.s3.amazonaws.com/0/142879/6126a390-ffbf-db00-092b-087755db4e9b.png)

`Height`プロパティに `250` を入力して<kbd>Enter</kbd>を押してください。
Windowの高さが変わります。
![wbg2-03.png](https://qiita-image-store.s3.amazonaws.com/0/142879/acff5fe2-ff60-5f97-3a74-d9224313939f.png)

プロパティの検索ボックス右端の×ボタンを押してフィルタリングを解除してください。
![wbg2-04.png](https://qiita-image-store.s3.amazonaws.com/0/142879/edc5d824-d5b8-583f-9760-32d0e5912944.png)

## ◇ウィンドウのその他の設定

引き続きWindowのプロパティを設定していきます。
`ResizeMode`を`CanResizeWithGrip`に設定してリサイズグリップを表示させます。
![wbg2-05.png](https://qiita-image-store.s3.amazonaws.com/0/142879/36c898e7-b4c9-fece-4bbb-201961517c7f.png)

リサイズグリップが表示されて、ウィンドウをサイズ変更できることが視覚的にわかりやすくなりました。
ついでに`ShowInTaskbar`のチェックを外して、実行時にタスクバーに表示しないようにします。
![wbg2-06.png](https://qiita-image-store.s3.amazonaws.com/0/142879/b0306078-f820-ca9c-09f2-c5e87ab4df5f.png)

`Title`プロパティに`時計`を入力します。これはタイトルバーに表示される内容です。
`Topmost`にチェックを入れます。ウィンドウが前面に表示されるようになります。
![wbg2-07.png](https://qiita-image-store.s3.amazonaws.com/0/142879/7841bbf8-e150-3583-031b-28b5efa2aa05.png)

変更したプロパティを元に戻したい場合やクリアしたい場合は、
プロパティ右端の`□`をクリックし、メニューから「リセット」を選択します。
![wbg2-08.png](https://qiita-image-store.s3.amazonaws.com/0/142879/e218da66-884d-1b30-fedc-24fc6cb9f314.png)

## ◇Viewboxの追加

次は`Viewbox`というものを追加します。
画面デザイナでタイトルバーの下、白い部分をクリックします。
そうすると`Grid`が選択状態になります。
左のツールボックスの検索ボックスに `v` と入力します。
`Viewbox`が見つけやすくなったので`Grid`にドラッグ＆ドロップします。
![wbg2-09.png](https://qiita-image-store.s3.amazonaws.com/0/142879/37469598-e5dc-f267-9c8e-1929da882a6d.png)

XAMLの`Grid`タグの中に`Viewbox`が入ったでしょうか？
失敗した場合は<kbd><kbd>Ctrl</kbd> + <kbd>Z</kbd></kbd>で前の状態に戻ります。
うまくいったらツールボックスの検索ボックス×ボタンを押してフィルタリングを解除しておきます。
間違ってツールボックス自体の×ボタンを押してしまわないように。
その場合はメニューの「表示」 - 「ツールボックス」で再表示できます。
![wbg2-10.png](https://qiita-image-store.s3.amazonaws.com/0/142879/57798e7a-c8c0-6b34-77dd-4e1e2cec409d.png)

## ◇TextBlockの追加

ツールボックスから`TextBlock`を`Viewbox`の上にドラッグ＆ドロップします。
![wbg2-11.png](https://qiita-image-store.s3.amazonaws.com/0/142879/503e132c-2d39-3bf6-9f5b-b3c3bac43a22.png)
XAMLの階層構造は

* Grid
    * Viewbox
        * TextBlock

となっているでしょうか？

## ◇表示を整える

`Viewbox`の不要なプロパティを削除します。
XAMLで画像の通り、全プロパティを範囲選択して<kbd>Delete</kbd>キーで削除します。
![wbg2-12.png](https://qiita-image-store.s3.amazonaws.com/0/142879/0d65456d-4c67-76e8-5593-3dc841a9c3bc.png)

幅や高さ指定などが削除されたのでウィンドウサイズいっぱいに表示されました。
これでウィンドウサイズに合わせて拡大縮小するテキストが実現できました。
![wbg2-13.png](https://qiita-image-store.s3.amazonaws.com/0/142879/9e5c75a5-3304-0e3f-3bba-58de28db5d61.png)

`TextBlock`の`TextWrapping`プロパティは削除し、`Text`プロパティは`00:00:00`を設定します。
![wbg2-14.png](https://qiita-image-store.s3.amazonaws.com/0/142879/5a85810d-ec3c-a2ab-144b-82f4f50d8d4a.png)

## ◇ロジック部分記述

### <i class="fa fa-square-o" style="font-size:1em;"></i> usingの追加

コードビハインドを開きます。`MainWindow.xaml.cs`のタブをクリックするか、ソリューションエクスプローラーで該当ファイルをダブルクリックします。
usingに`System.Windows.Threading;`を追加します。

![wbg2-15.png](https://qiita-image-store.s3.amazonaws.com/0/142879/a22535a0-fcc4-be08-2f0c-d2de7ceca5ab.png)

これは後で出てくる`DispatcherTimer`クラスや`DispatcherPriority`列挙型が属する名前空間（識別名）です。
usingを書いておくと`System.Windows.Threading.DispatcherTimer`と書くところを、`DispatcherTimer`と省略して書けるようになります。

<details>
<summary>📝名前空間の例</summary>
<div>

```CSharp:名前空間の例
using System;
using 日本.静岡県.浜松市;

namespace NameSpaceSample
{
    class Program
    {
        static void Main()
        {
            日本.東京都.足立区.田中一郎.SayHello();

            // usingしているので「日本.静岡県.浜松市」は省略可
            日本.静岡県.浜松市.田中一郎.SayHello();

            // usingしている「日本.静岡県.浜松市」の田中一郎が使用される
            田中一郎.SayHello();
        }
    }
}

namespace 日本.東京都.足立区
{
    static class 田中一郎
    {
        public static void SayHello()
        {
            Console.WriteLine("田中一郎です。");
        }
    }
}

namespace 日本.静岡県.浜松市
{
    static class 田中一郎
    {
        public static void SayHello()
        {
            Console.WriteLine("こんにちは。");
        }
    }
}
```

</div>
</details>

### <i class="fa fa-square-o" style="font-size:1em;"></i> タイマーのフィールド（変数）追加
class定義の中に、以下のタイマー変数を入力します。

```CSharp
private DispatcherTimer timer;
```

そして変数定義の上で/（スラッシュ）を3つ入力します。
![wbg2-16.png](https://qiita-image-store.s3.amazonaws.com/0/142879/bcb644a4-48c7-1ac5-8f6f-96027ce68430.png)

そうすると図のようなコメントが作成されます。
summaryタグの間、カーソル位置のあるところから`時刻表示用タイマー`とコメント内容を入力してください。

![wbg2-17.png](https://qiita-image-store.s3.amazonaws.com/0/142879/158292d3-0541-67ef-a930-c8a3dff7eecc.png)

### <i class="fa fa-square-o" style="font-size:1em;"></i> タイマー生成メソッド（関数）の追加

コンストラクタ定義（`public MainWindow()`）の下あたりに以下のコードを入力してください。

```CSharp
private DispatcherTimer CreateTimer()
{
}
```

![wbg2-18.png](https://qiita-image-store.s3.amazonaws.com/0/142879/20b8a18f-3e62-7f7c-5ee6-4545f1a508bc.png)

CreateTimerメソッド（関数）の上でも///でコメントを書いてください。
そして中にタイマーを生成して返す処理を書きます。

```CSharp
public partial class MainWindow : Window
{
    /// <summary>
    /// 時刻表示用タイマー
    /// </summary>
    private DispatcherTimer timer;

    public MainWindow()
    {
        InitializeComponent();
    }

    /// <summary>
    /// タイマー生成処理
    /// </summary>
    /// <returns>生成したタイマー</returns>
    private DispatcherTimer CreateTimer()
    {
        // タイマー生成（優先度はアイドル時に設定）
        var t = new DispatcherTimer(DispatcherPriority.SystemIdle);

        // タイマーイベントの発生間隔を300ミリ秒に設定
        t.Interval = TimeSpan.FromMilliseconds(300);

        // タイマーイベントの定義
        t.Tick += (sender, e) => {
            // タイマーイベント発生時の処理をここに書く

            // 現在の時分秒をテキストに設定
            textBlock.Text = DateTime.Now.ToString("HH:mm:ss");
        };

        // 生成したタイマーを返す
        return t;
    }
}
```

### <i class="fa fa-square-o" style="font-size:1em;"></i> タイマー生成メソッドの呼び出し処理追加

コンストラクタの`InitializeComponent`の後に、`CreateTimer`を呼び出して結果をtimer変数に入れる処理を書きます。
コンストラクタはクラス生成時に呼び出されます。
つまりMainWindowの初期処理です。

```CSharp
public MainWindow()
{
    InitializeComponent();
    
    // タイマー生成
    timer = CreateTimer();
}
```

書き終わったら`CreateTimer`をマウスでポイントしてみてください。
メソッド定義と///で書いたコメントがポップアップで表示されます。
![wbg2-19.png](https://qiita-image-store.s3.amazonaws.com/0/142879/4d29aa4d-63fa-8cc8-1bca-773d06a31e98.png)

`timer`の方もマウスでポイントするとコメント内容が表示されます。
ソースを読む助けになるので///コメント（ドキュメントコメント）は積極的に書きましょう。
![wbg2-20.png](https://qiita-image-store.s3.amazonaws.com/0/142879/d9d22748-78ba-db42-bc7d-cb62e8fcc43d.png)

ここまでで、タイマーを生成する処理、タイマーが起動したら一定間隔で現在時刻を表示する処理が書けました。
最後にタイマーを起動する処理を書きます。
タイマー生成の後すぐに起動してもよいのですが、イベント処理の練習のため、時刻のテキストをクリックしたらタイマー起動するようにしてみます。

### <i class="fa fa-square-o" style="font-size:1em;"></i> タイマー起動処理の追加

XAMLの方に戻り、`TextBlock`タグを選択してください。
画面デザイナの方で選択してもいいですが、画面が入り組んでくると目的のコントロールを選択しづらくなってくるのでXAMLのタグを選択した方が確実です。
![wbg2-21.png](https://qiita-image-store.s3.amazonaws.com/0/142879/2b21dbe0-d085-49ef-363d-8334190a1b3d.png)
プロパティウィンドウの右上に稲妻のマークがあるのでクリックしてください。
TextBlockで使えるイベントの一覧が出てきます。
（前回は画面デザイナ上でボタンをダブルクリックしてイベント処理を書きましたが、もちろんボタンもこの方法で書けます。）

TextBlockにはClickイベントが無いので、`MouseDown`イベントを使うことにします。
MouseDownのテキストボックスをダブルクリックしてください。
イベントハンドラが自動的に作成されます。
テキストボックスに文字を直接打ち込んで任意の名前を付けることもできます。

イベントハンドラの中に`timer.Start();`と入力してください。
![wbg2-22.png](https://qiita-image-store.s3.amazonaws.com/0/142879/b60a55c3-841a-4e7f-c185-ce64a58337a0.png)

### <i class="fa fa-square-o" style="font-size:1em;"></i> お掃除

最後に、コードビハインドに戻ってエディタ部分の適当な場所で右クリック、「usingの整理」 - 「usingの削除と並べ替え」を実行して、使っていないusing定義を消します。
ソースをすっきりさせたい人はやってください。
まだ色々コード追加する予定の人はそれが完全に終わってからの方がいいです。
![wbg2-23.png](https://qiita-image-store.s3.amazonaws.com/0/142879/8d5d8158-ab67-e1cf-64cd-702c595117ab.png)

すっきり。
![wbg2-24.png](https://qiita-image-store.s3.amazonaws.com/0/142879/9d61f955-d2cc-1d29-0aa1-2c92ec0979be.png)

最終的なXAMLも載せておきます。

```Xml:MainWindow.xaml
<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication2"
        mc:Ignorable="d"
        Title="時計" Height="250" Width="525" ResizeMode="CanResizeWithGrip" ShowInTaskbar="False" Topmost="True">
    <Grid>
        <Viewbox>
            <TextBlock x:Name="textBlock" Text="00:00:00" MouseDown="textBlock_MouseDown"/>
        </Viewbox>

    </Grid>
</Window>
```

# ■動かしてみる
ツールバーで開始ボタンを押すとビルドされてプログラムが起動します。
![wbg2-25.png](https://qiita-image-store.s3.amazonaws.com/0/142879/cad1604e-a41d-8cb5-91cb-ff8b9c5cb35e.png)

`00:00:00`の部分をマウスでクリックするとタイマーが動き出し、現在時刻が表示されるようになります。
![wbg2-26.png](https://qiita-image-store.s3.amazonaws.com/0/142879/75b43d26-231f-7690-6ef5-bea4450d15fb.png)

ウィンドウを拡大・縮小するとウィンドウサイズに合わせて時刻の文字サイズも変わります。
![wbg2-27.png](https://qiita-image-store.s3.amazonaws.com/0/142879/361e6f87-2e26-55c3-5f0c-52f92972b5b7.png)

# ■配布するとき
Visual Studio上からでなく、普通にexeとして起動したい場合や、他の人に配布するときはReleaseビルドで作成したexeを使ってください。

`Debug`ビルドから`Release`ビルドに切り替えて、メニューの「ビルド」 - 「ソリューションのビルド」を実行します。
![wbg2-28.png](https://qiita-image-store.s3.amazonaws.com/0/142879/6bbbf58d-c768-8356-b63a-91dfe1a49161.png)

ソリューションエクスプローラーでプロジェクトを右クリックし、「エクスプローラーでフォルダを開く」を選択します。
![wbg2-29.png](https://qiita-image-store.s3.amazonaws.com/0/142879/3e5f6285-bd15-f5e8-4cd5-fc0cf5ff5aad.png)

`bin\Release`フォルダを開き、 【プロジェクト名】.exe を配布してください。
この例だと、`WpfApplication2.exe`です。
他のファイルは不要です。
![wbg2-30.png](https://qiita-image-store.s3.amazonaws.com/0/142879/985f1f44-ae62-da7b-c397-e0f0ac034a3d.png)

おしまい

---
# ■参考サイト
* [Viewbox クラス - MSDN](https://msdn.microsoft.com/ja-jp/library/system.windows.controls.viewbox.aspx)
* [DispatcherTimer クラス - MSDN](https://msdn.microsoft.com/ja-jp/library/system.windows.threading.dispatchertimer.aspx)
* [DispatcherPriority 列挙型 - MSDN](https://msdn.microsoft.com/ja-jp/library/system.windows.threading.dispatcherpriority.aspx)

---

[＜ 前の記事](http://qiita.com/Kosen-amai/items/f9e3df2aa80363f5af5b "Windows GUIプログラミング入門1 メッセージボックス表示") 　 [次の記事 ＞](http://qiita.com/Kosen-amai/items/1ef3685a0178e134320a "Windows GUIプログラミング入門3 各種設定")