タイトルは某ECサイトを参考にしました。なので中身は胡散臭いです:-)
令和7(2025)年になってからも.NET10に向けに若干の変更があったようですが、いまだMicrosoft Copilotですら「ソリューションを右クリックして参照の追加で云々...」とぬかしやがるので1将来でもなるべくコード修正しないで済むようにまとめておきます。
以下の記事を元にさせていただきました。ありがとうございます。
アイコンの準備
変更ありません。いつものようにリソースとして使えるようにしておいてください。
Formクラスの準備
これも変更ありません。.csproj
ファイルに<UseWindowsForms>True</UseWindowsForms>
を追加してください。
<PropertyGroup>
:
<UseWPF>true</UseWPF>
<UseWindowsForms>True</UseWindowsForms>
</PropertyGroup>
「あいまいな参照」の解決
ここに変更というか追加があります。.csprojに上記を追加した後WPFとWindows Formsに同名のクラスがあってもいままではいい感じに解決してくれていたようですが、今では参照の解決方法が変わったのか同名のクラスは「XXXとYYY間のあいまいな参照です」と言われまくります2。
破壊的変更 - WPF と WinForms の両方を参照するアプリケーションは、MenuItem 型と ContextMenu 型のあいまいさを解消する必要があります
エラーが出たところひとつひとつ使いたい方で完全修飾してあげればいいのですが、ソース本体が非常に見づらくなるので数が多い場合はusingを使ってエリアスを設定してあげます。
using Application = System.Windows.Application;
//System.Windows.Forms.Applicationとバッティングするため。どちらを使うのかエリアスを使って明示する
//以下必要なだけ追加
:
タスクトレイアイコンを作る
Formsのコントロール部品ですのでXAMLには書けないしFormsアプリのようにデザイナーは使えないのでコードビハインドにゴリゴリ記述します。
自分の場合、(起動時から最小化でスタートすることを想定していないため?)コンストラクタから設定するようにしました。
各メニューアイテムの作成
後でメニューの追加・削除が楽なようにDictionaly
、List
を使用して連続的に登録しています。Listに格納しているMenuItem名は例ですので適宜お好きな名前(表示)に変更してください。"-"
はセパレータを表示します。
:
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。
同時にRefrection
でitem
を使って各メニューのClickのイベントハンドラを登録します。その際、表示名から不要なスペースを削除してメソッド名を作成し、private
でも検索できるようBindingFlags.Instance | BindingFlags.NonPublic
フラッグを立てます。
:
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。
:
var icon = GetResourceStream(new Uri("icon.ico", UriKind.Relative)).Stream;
notifyIcon = new NotifyIcon()
{
Text = "Test",
Visible = true,
Icon = new Icon(icon),
ContextMenuStrip = toolStripMenu,
};
}
対応するメソッドの追加
ここは中身がそれぞれ違うのでひとつひとつ手で作成しますが、メソッドの名前は追加したときのものに合わせてください。
終了時は念のためNotifyIcon
もDispose()
しておきます。
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();
}