0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WPFでタスクトレイアイコンを使う【令和7年最新版】

Last updated at Posted at 2025-04-20

タイトルは某ECサイトを参考にしました。なので中身は胡散臭いです:-)

令和7(2025)年になってからも.NET10に向けに若干の変更があったようですが、いまだMicrosoft Copilotですら「ソリューションを右クリックして参照の追加で云々...」とぬかしやがるので1将来でもなるべくコード修正しないで済むようにまとめておきます。
以下の記事を元にさせていただきました。ありがとうございます。

アイコンの準備

変更ありません。いつものようにリソースとして使えるようにしておいてください。

Formクラスの準備

これも変更ありません。.csprojファイルに<UseWindowsForms>True</UseWindowsForms>を追加してください。

foobar.csproj
<PropertyGroup>
    :
    <UseWPF>true</UseWPF>
    <UseWindowsForms>True</UseWindowsForms>
</PropertyGroup>

「あいまいな参照」の解決

ここに変更というか追加があります。.csprojに上記を追加した後WPFとWindows Formsに同名のクラスがあってもいままではいい感じに解決してくれていたようですが、今では参照の解決方法が変わったのか同名のクラスは「XXXとYYY間のあいまいな参照です」と言われまくります2

破壊的変更 - WPF と WinForms の両方を参照するアプリケーションは、MenuItem 型と ContextMenu 型のあいまいさを解消する必要があります

エラーが出たところひとつひとつ使いたい方で完全修飾してあげればいいのですが、ソース本体が非常に見づらくなるので数が多い場合はusingを使ってエリアスを設定してあげます。

MainWindow.cs
using Application = System.Windows.Application;
//System.Windows.Forms.Applicationとバッティングするため。どちらを使うのかエリアスを使って明示する
//以下必要なだけ追加
:

タスクトレイアイコンを作る

Formsのコントロール部品ですのでXAMLには書けないしFormsアプリのようにデザイナーは使えないのでコードビハインドにゴリゴリ記述します。
自分の場合、(起動時から最小化でスタートすることを想定していないため?)コンストラクタから設定するようにしました。

各メニューアイテムの作成

後でメニューの追加・削除が楽なようにDictionalyListを使用して連続的に登録しています。Listに格納しているMenuItem名は例ですので適宜お好きな名前(表示)に変更してください。"-"はセパレータを表示します。

MainWindow.cs
:
using System.Reflection;

public partial class MainWindow : Window
{
    private NotifyIcon notifyIcon = new();
    private readonly Dictionary<string, ToolStripMenuItem> toolStripMenuItemsDict = [];

    public MainWindow()
    {
        InitializeComponent();

        SetNotifyIcon();
        :
    }
        
    private void SetNotifyIcon()
    {
        List<string> toolStripMenuItemsList = ["Config", "-", "Spectrum", "Level Meter", "Level Stream", "-", "Reload Device", "-", "Exit"];
        var toolStripMenu = new ContextMenuStrip();
        foreach (var item in toolStripMenuItemsList)
        {
            if (item != "-")
            {
                toolStripMenuItemsDict[item] = new ToolStripMenuItem()
                {
                    Text = item,
                    Image = null,
                };
                toolStripMenu.Items.Add(toolStripMenuItemsDict[item]);
                :

メニューを表示したときチェックマークを付けるところはtoolStripMenuItemsDict["Spectrum"].Checked = true;のように追加しておいてください。もちろん後でやってもOKです3

同時にRefrectionitemを使って各メニューのClickのイベントハンドラを登録します。その際、表示名から不要なスペースを削除してメソッド名を作成し、privateでも検索できるようBindingFlags.Instance | BindingFlags.NonPublicフラッグを立てます。

MainWindow.cs
                :
                toolStripMenuItemsDict[item].Click += (s, e) =>
                {
                    var method = GetType().GetMethod(item.Replace(" ", "") + "ToolStripMenuItem_Click", BindingFlags.Instance | BindingFlags.NonPublic);
                    method?.Invoke(this, [s, e]);
                };
            }
            else
            {
                toolStripMenu.Items.Add(new ToolStripSeparator());
            }
        }
        :

コンテキストメニューへの登録

toolStripMenuに各アイテムが追加出来たらNotifyIconをいつもどおり登録します4

MainWindow.cs
        :
        var icon = GetResourceStream(new Uri("icon.ico", UriKind.Relative)).Stream;
        notifyIcon = new NotifyIcon()
        {
            Text = "Test",
            Visible = true,
            Icon = new Icon(icon),
            ContextMenuStrip = toolStripMenu,
        };
    }

対応するメソッドの追加

ここは中身がそれぞれ違うのでひとつひとつ手で作成しますが、メソッドの名前は追加したときのものに合わせてください。
終了時は念のためNotifyIconDispose()しておきます。

MainWindow.cs
private void ConfigToolStripMenuItem_Click(object? sender, EventArgs e)
{
    ...
}

private void SpectrumToolStripMenuItem_Click(object? sender, EventArgs e)
{
    toolStripMenuItemsDict["Spectrum"].Checked = !toolStripMenuItemsDict["Spectrum"].Checked;
}
:
private void ExitToolStripMenuItem_Click(object? sender, EventArgs e)
{
    notifyIcon?.Dispose();
    Application.Current.Shutdown();
}

ちゃんと対応するメソッドに飛ぶか確認して、これで完成です。
スクリーンショット 2025-04-20 113602.png

  1. 最新のVisualStudio2022でWPFの場合にはそんな右クリックメニューすら出ない

  2. Point型やColor型、MessageBoxを使っただけで言われます。

  3. 当然ですがFormsなので.IsCheckedではありません。

  4. もしNotifyIcon自体にWindowサイズの復元などのイベント処理が必要な場合は適宜イベントハンドラも追加します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?