C#

【はじめてでも簡単】C#で初めてのWFPデスクトップアプリケーション

こんにちは。
今回はC#とVisualStudioを使って、シンプルなデスクトップタイマーを作ってみました。
25分カウントしたら5分休憩をはさむという、ポモドーロタイマーをデスクトップ画面に表示できるアプリケーションです。
仕事中にPCに表示させて使おうかなとか思ってつくりました。

こちらは完成したアプリケーション。

desktop-potimer(zipファイル)
上のリンクからダウンロードしたzipファイルを解凍して、中に入っているtimer.exeを実行すると使用可能です。

今回は初めてのC#プログラミングでしたが、VisualStudioが想像以上に便利で使いやすく、C#という言語も簡単に思い通りの実装ができたので、とても感動しました。
もともとはXamarinでスマホアプリを作りたくてC#の勉強を始めたのですが、これまで学んだどの言語よりも便利で使いやすく、楽しくコードを書ける言語だなと感じました。

さて、今回は初めてのC#プログラミングではありますが、インストールやセットアップといった内容は割愛します。
環境構築は非常に直感的に行うことができ、特に何も読まなくても普通にVisualStudioでデスクトップアプリケーションを作成する準備をすることができました。
さすがあのWindowsのMicrosoftといった感じです。

RailsやJavaの環境構築に手こずった経験のある僕からすると、C#は初心者でも手軽にプログラミングを始められる素晴らしいプラットフォームがあるなと感じます。

それはさておき。
今回の記事では、個人的に苦労した、ビルドしたプログラムで

と、

についてまとめます。

もくじ

  1. ソースコード
  2. ローカルの画像を表示する
  3. 実行ファイル形式にパッケージングする
  4. まとめ
  5. 参考

ソースコード

まずはコードを貼っておきます。
UIのxamlソースは割愛しますね。

//MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace timer
{
    public partial class MainWindow : Window
    {
        private DispatcherTimer dispatcherTimer;
        int m = 25;
        int s = 0;
        string min;
        string sec;
        string checker = "active";

        public MainWindow()
        {
            InitializeComponent();
            dispatcherTimer = new DispatcherTimer(DispatcherPriority.Normal);
            dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
            
        }
        void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            switch (checker)
            {
                case "active":
                    imagebox.Source = new BitmapImage(new Uri(System.AppDomain.CurrentDomain.BaseDirectory + "study.png"));
                    break;
                case "break":
                    imagebox.Source = new BitmapImage(new Uri(System.AppDomain.CurrentDomain.BaseDirectory + "breaktime.png"));
                    break;
            }
            
            //カウントダウン分岐
            if (s == 0)
            {
                if (m == 0)
                {

                    if (checker == "active")
                    {
                        checker = "break";
                        m = 5;
                        s = 0;
                    }
                    else
                    {
                        checker = "active";
                        m = 25;
                        s = 0;
                    }
                }
                else
                {
                    m -= 1;
                    s = 59;
                }
            }
            else
            {
                s -= 1;
            }

            //タイマー表示
            min = m.ToString();
            sec = s.ToString();

            if(m < 10)
            {
                min = "0" + m.ToString();
            }
            if (s < 10)
            {
                sec = "0" + s.ToString();
            } 

            timebox.Content = string.Format("{0}:{1}", min,sec);
        }

        private void Button_Click_left(object sender, RoutedEventArgs e)
        {
            dispatcherTimer.Start();
        }
        private void Button_Click_right(object sender, RoutedEventArgs e)
        {
            dispatcherTimer.Stop();
            
            //リセット
            m = 25;
            s = 0;
            checker = "active";
            timebox.Content = string.Format("25:00", min, sec);
        }

    }

    internal class dispatcherTimer
    {
    }
}

ローカルの画像を表示する

今回最も苦戦したのはこのローカルディレクトリの画像をビルド後もアプリケーションに表示させることでした。
普通にImage要素のSourceに相対パスを指定すると、プレビュー画面では画像が正しく表示されるのですが、ビルドするとなぜかエラーになってしまいました。

解決策としては、

imagebox.Source = new BitmapImage(new Uri(System.AppDomain.CurrentDomain.BaseDirectory + "study.png"));

といった形で、exeファイルを実行したときのディレクトリを取得してあげればなんとかなりました。
どうやらビルドされて実行ファイル形式になると、カレントディレクトリを’./○○’で参照することができないようです。

その辺の詳しいことは今後の課題ですね…。

実行ファイル形式にパッケージングする

作成したアプリケーションをパッケージングする方法はいくつかありましたが、一番簡単だったのはWindowsアプリケーションパッケージプロジェクトを使った方法でした。
詳しいやり方については、Microsoft公式のPackage a desktop application by using Visual Studioが参考になります。

ざっくりと流れをメモしておくと、

① Windowsアプリケーションパッケージプロジェクトをソリューションに追加(なければVSInstallerからインストール)
② 上記プロジェクトの『アプリケーション』を右クリックして、参照の追加を行い、パッケージングしたいプロジェクトにチェックをつける
③ ビルドでパッケージングされたディレクトリが生成される

という3ステップで簡単にビルドが完了します。
感動的なくらい簡単にできました。

まとめ

今回初めてC#のアプリケーション開発にチャレンジしてみました。
使用も環境もわからないことばかりですが、それでもVisualStudioの直感的な操作性に助けられ、なんとたった数時間でWPFアプリケーションの作成からパッケージングまで行うことができました。
もともとはXamarinでスマホアプリ開発をしたいと思って始めたC#ですが、思いのほかハマってしましました。
とても楽しい。

参考

Package a desktop application by using Visual Studio