WPFでタスクトレイ常駐アプリを作る

WPFでタスクトレイ（通知領域）常駐アプリを作ろうとしたら想像以上に手間がかかったので備忘録として初投稿です。
## 動作環境
* Visual Studio 2019
* .NET Core3.1（.NET5.0でも可）

## 事前準備
新しいプロジェクトの作成からWPF App(.NET)を作成しておく。
## アイコンの準備
まずタスクトレイにアイコンを表示させるためicoファイルを作成しておきます。
[Inkscape](https://inkscape.org/ja/release/inkscape-1.0.1/)等のドローソフトを使うのが一番楽だと思います。
ちなみに[ウインドウズ標準形式アイコン作成](https://ao-system.net/winicon/)等のサイトを使用すれば簡単にマルチアイコンが作成できます。
icoファイルを作成し終えたらソリューションエクスプローラから既存の項目として追加します。
![既存の項目.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/807403/75322911-bb56-0d56-0ec5-3461445f7ea8.png)
その後プロパティのビルドアクションをリソースに変更してください。
![icoファイルのプロパティ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/807403/db4e320a-794e-266e-bc09-a5b758cdd9c9.png)
## Formクラスの準備
多分一番躓きやすいポイントだと思います。
他のサイトだと参照を追加してくださいとか色々書かれてると思いますが今はプロジェクトファイルの`<PropertyGroup>`内に

```:.csproj
<UseWindowsForms>true</UseWindowsForms>
```
と追記することでFormクラスを扱うことができます。
## WFPでタスクトレイを作る
長い準備も終わったため、いよいよタスクトレイのプログラミングです。App.xaml.csのOnStartupメソッドに以下のように書いてください。

```c#:App.xaml.cs
public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            var icon = GetResourceStream(new Uri("icon.ico", UriKind.Relative)).Stream;
            var notifyIcon = new System.Windows.Forms.NotifyIcon
            {
                Visible = true,
                Icon = new System.Drawing.Icon(icon),
                Text = "タスクトレイ常駐アプリのテストです"
            };
        }
    }
```
変数iconのicon.icoには自分が用意したicoファイルの名前を使用してください。
Textはタスクトレイのアイコンにマウスオーバーしたときに出るツールチップのテキストです。
![タスクトレイのツールチップ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/807403/f4b79480-6757-928c-0105-e5c25bc34dd9.png)
## コンテキストメニューの追加

```c#:App.xaml.cs
public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            var icon = GetResourceStream(new Uri("icon.ico", UriKind.Relative)).Stream;
            var menu = new System.Windows.Forms.ContextMenuStrip();
            menu.Items.Add("終了", null, Exit_Click);
            var notifyIcon = new System.Windows.Forms.NotifyIcon
            {
                Visible = true,
                Icon = new System.Drawing.Icon(icon),
                Text = "タスクトレイ常駐アプリのテストです",
                ContextMenuStrip = menu
            };
            notifyIcon.MouseClick += new System.Windows.Forms.MouseEventHandler(NotifyIcon_Click);
        }

        private void NotifyIcon_Click(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                var wnd = new MainWindow();
                wnd.Show();
            }
        }

        private void Exit_Click(object sender, EventArgs e)
        {
            Shutdown();
        }
    }
```
Items.Addでコンテキストメニューを追加します。
![タスクトレイのコンテキストメニュー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/807403/dbab1dff-292c-6720-9a01-a9507f92716f.png)
またMouseClickイベントで左クリックの動作を決めることもできます。
## 実行時にウィンドウが表示されないようにする
これでタスクトレイに表示をすることができましたが、常駐ソフトなのに実行時にいちいちウィンドウが表示されるのは鬱陶しいため実行時にはウィンドウが表示されないようにします。
App.xamlのStartupUriを削除すればウィンドウは表示されずに動作します。
![スタートアップの削除.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/807403/080481d9-8310-f1e5-bc51-a0b04282443e.png)
## まとめ
WPFでタスクトレイを扱うには`<UseWindowsForms>true</UseWindowsForms>`が必要。
OnStartupメソッドに処理を書き込む。
ついでにApp.xamlのStartupUriを削除する。
## 参考記事
[Windowsのタスクトレイ常駐型アプリを作る](https://shuhelohelo.hatenablog.com/entry/2019/11/24/185838)
[Using System.Windows.Forms classes in a .net core 3.0 preview9 project](https://stackoverflow.com/questions/57908184/using-system-windows-forms-classes-in-a-net-core-3-0-preview9-project)
[[WPF] リソースの画像から BITMAP オブジェクトを生成する](https://rksoftware.wordpress.com/2016/10/14/001-55/)
[C#のタスクトレイ常駐アプリの作り方のご紹介！](https://www.fenet.jp/dotnet/column/environment/4527/)
[C# Right mouse click on button does not raise mouseclick event](https://stackoverflow.com/questions/8180234/c-sharp-right-mouse-click-on-button-does-not-raise-mouseclick-event)