我相信你现在已经找到了答案,但是没有“解决方案”并且有很多观点,所以让我给出一个关于如何解决这个问题的选项。
这实际上是一个非常常见的架构,像您这样的 Windows 使用新的 Windows 11 MAUI API 的其他技术,但是,用户并没有跳上“瓷砖”设计,时间线充满了点击诱饵而不是可靠的方式与用户“交谈”。
有几种方法可以做到这一点,您可以在服务代码中启动托盘图标,使其成为仅限 Windows 的服务
基本上,您正在考虑将您的服务与 System.Windows.Forms.NotifyIcon 和TrayIcon.Visible属性相结合
使用 NotifyIcon 您可以执行以下操作:
class MyTray:IDisposable
{
NotifyIcon ni;//needs disposed
public ()
{
ni= new NotifyIcon()
//use a helper class to generate a context menu for the tray icon if there is a menu like start, stop ...
ni.ContextMenuStrip = new MyContextMenus(menuDataContext).Create();
}
}
那么当你调用它时,你会:
ni.Icon = Resources.Icon_red;
ni.Text = "Some ballon Text";
//使托盘图标可见 ni.Visible = true; 用户可以与托盘菜单上的图标进行交互,这里是 MyContextMenu(backingField) 创建的示例:
public ContextMenuStrip Create()
{
// Add the default menu options.
menu = new ContextMenuStrip();
ToolStripMenuItem item;
ToolStripSeparator sep;
item = new ToolStripMenuItem
{
Text = "License",
Image = Resources.contract
};
item.Click += new EventHandler(License_Click);
menu.Items.Add(item);
// About.
item = new ToolStripMenuItem
{
Text = "Service Status",
Image = Resources.data_green1
};
item.Click += new EventHandler(Status_Click);
menu.Items.Add(item);
// Separator.
sep = new ToolStripSeparator();
menu.Items.Add(sep);
//rule engine editor
item = new ToolStripMenuItem
{
Text = "Rule Engine Editor",
Image = Resources.data_edit1
};
item.Click += new System.EventHandler(Editor_Click);
menu.Items.Add(item);
// Separator.
sep = new ToolStripSeparator();
menu.Items.Add(sep);
// Exit.
item = new ToolStripMenuItem
{
Name = "mnuClose",
Text = "Close",
Image = Resources.data_down
};
item.Click += new EventHandler(Exit_Click);
menu.Items.Add(item);
return menu;
}
或者解耦它,就像在这个示例中一样,服务可以在任何支持 .net 的操作系统上,并通过 ProtoBuf、Sockets WCF 或命名管道等协议进行通信。
也许是一种“更好”的方式来做到这一点
看看这篇文章
此示例使用名称管道(网络连接)与使用 NuGet 包和 WPF 作为演示平台的应用程序对话。
服务器与任何收听管道的人对话,如下所示:
using H.Pipes;
using H.Pipes.Args;
using NamedPipesSample.Common;
namespace NamedPipesSample.WindowsService
{
public class NamedPipesServer : IDisposable
{
const string PIPE_NAME = "samplepipe";
private PipeServer<PipeMessage> server;
public async Task InitializeAsync()
{
server = new PipeServer<PipeMessage>(PIPE_NAME);
server.ClientConnected += async (o, args) => await OnClientConnectedAsync(args);
server.ClientDisconnected += (o, args) => OnClientDisconnected(args);
server.MessageReceived += (sender, args) => OnMessageReceived(args.Message);
server.ExceptionOccurred += (o, args) => OnExceptionOccurred(args.Exception);
await server.StartAsync();
}
private void OnClientConnected(ConnectionEventArgs<PipeMessage> args)
{
Console.WriteLine($"Client {args.Connection.Id} is now connected!");
await args.Connection.WriteAsync(new PipeMessage
{
Action = ActionType.SendText,
Text = "Hi from server"
});
}
private void OnClientDisconnected(ConnectionEventArgs<PipeMessage> args)
{
Console.WriteLine($"Client {args.Connection.Id} disconnected");
}
//...
}
}
如果您按照示例进行操作,则充当托盘图标的 WPF 应用程序将像这样“下降”:
public async Task InitializeAsync()
{
if (client != null && client.IsConnected)
return;
client = new PipeClient<PipeMessage>(pipeName);
client.MessageReceived += (sender, args) => OnMessageReceived(args.Message);
client.Disconnected += (o, args) => MessageBox.Show("Disconnected from server");
client.Connected += (o, args) => MessageBox.Show("Connected to server");
client.ExceptionOccurred += (o, args) => OnExceptionOccurred(args.Exception);
await client.ConnectAsync();
await client.WriteAsync(new PipeMessage
{
Action = ActionType.SendText,
Text = "Hello from client",
});
}