# 流程
- 创建一个
STA 线程 - 调用
Application.Run 方法 - 使 UI
DPI感知 - 创建
NotifyIcon 对象并使 Visible 为 true - 暴露 UI 线程调度器的
Invoke 方法
# 创建一个 STA 线程
1 2 3 4 5
| UIThread = new Thread(() => { }); UIThread.SetApartmentState(ApartmentState.STA); UIThread.Start();
|
# 调用 Application.Run 方法
1 2 3 4 5 6 7 8
| UIThread = new Thread(() => { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(); }); UIThread.SetApartmentState(ApartmentState.STA); UIThread.Start();
|
# 使 UI DPI 感知
控制台程序是无法使用 manifest 文件来配置 DPI 感知的,我们使用 API 的方法来控制 DPI 感知
1 2 3 4 5 6 7 8 9 10 11 12 13
| UIThread = new Thread(() => { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); + SetProcessDPIAware(); + Application.Run(); }); UIThread.SetApartmentState(ApartmentState.STA); UIThread.Start();
+[DllImport("user32.dll")] +public static extern bool SetProcessDPIAware();
|
# 创建 NotifyIcon 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| UIThread = new Thread(() => { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); SetProcessDPIAware(); + NotifyIcon = new NotifyIcon(); + NotifyIcon.Icon = new Icon(new MemoryStream(Convert.FromBase64String(Resources.TaskBarIconResources.IconBase64))); + var menu = new ContextMenuStrip(); + NotifyIcon.ContextMenuStrip = menu; + menu.Items.Add(new ToolStripMenuItem { Text = "框架版本: 1.0.0.0" }); + ToolStripMenuItem reloadItem = new() { Text = "重载插件" }; + reloadItem.Click += async (a, b) =>// 使用异步防止卡死托盘 + { + await Task.Run(() => + { + if (MessageBox.Show("确定要重载插件吗?", "嗯?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + { + PluginManagerProxy.Instance.ReloadAllPlugins(); + } + }); + }; + menu.Items.Add(reloadItem); + + NotifyIcon.Visible = true; // 此时托盘将会出现 Application.Run(); }); UIThread.SetApartmentState(ApartmentState.STA); UIThread.Start();
[DllImport("user32.dll")] public static extern bool SetProcessDPIAware();
|
# 暴露 UI 线程调度器的 Invoke 方法
当在线程外部需要更新托盘的内容时,需要切换线程至 UI 线程才可操作控件。我们将创建的 NotityIcon 对象暴露到属性即可通过调用控件的 Invoke 方法来切换线程至 UI 线程
1 2 3 4 5 6 7 8 9 10 11
| private static void Invoke(Action action) { if (NotifyIcon.ContextMenuStrip != null && NotifyIcon.ContextMenuStrip.InvokeRequired) { NotifyIcon.ContextMenuStrip.BeginInvoke(action); } else { action.Invoke(); } }
|