栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

C# .NET 给Web项目(MVC,RazorPage,Blazor等)添加一个系统托盘图标,替代Winform,WPF,Electron,QT,WebView2,Tauri 等桌面程序

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C# .NET 给Web项目(MVC,RazorPage,Blazor等)添加一个系统托盘图标,替代Winform,WPF,Electron,QT,WebView2,Tauri 等桌面程序

效果图 功能
  1. 添加任务栏图标 ( 托盘图标 )

    • 添加右键菜单 (打开浏览器 / 控制台 /退出)

    • 双击打开控制台,

  2. 启动一个web服务器 ( MVC , Razor , Blazor 等等 )

  3. web服务器启动成功,自动打开浏览器页面

  4. 持续后台运行

由于使用 Winform 实现, 所以只支持windows

开发环境
  • VS 2022
  • .NET 6
代码 .csproj (项目的配置文件)

! 非常重要 !

// 移除
// net6.0

// 改为 ↓
net6.0-windows // 修改
true // 添加

有这两个才能在web项目中使用WinForm类库,
否则会找不到 System.Windows.Forms 类库


完整代码如下:
ImplicitUsings 可以在编译时引入框架约定的通用的命名空间,
当然其中也包括了 System.Windows.Forms


			
		net6.0-windows
		true
		enable
		enable
	

	
	  
	    Always
	  
	


Program.cs
public static class Program
{
    //[STAThread]
    static void Main(string[] args)
    {

#if DEBUG
        WebProgram.RunWebHost(args);
#else
        Console.Title = "我的程序名";

        // 初始化 托盘图标 和 隐藏控制台
        NotifyIconUtils.Init();

        // 新开一个线程运行 web服务器
        Task.Run(() => WebProgram.RunWebHost(args));

        // 消息循环, 处理托盘图标的点击操作, 并保持程序的持续运行, 
        // 使用 Application.Exit() 退出
        Application.Run();
#endif
    }
}
WebProgram.cs

非常普通的一个 .NET 6 WebHost,
直接把原来的代码复制过来就可以,
最后直接调用 RunAndStartedOpenBrowser
或者 自己去注册ApplicationStarted的事件

当前代码例子使用的是 Blazor
当然也可以用 MVC , RazorPage 等等
与任何框架都无关,
甚至你可以通过C#启动一个React, Vue 项目
也可以添加很多的右键菜单项,去做更多的功能

using MyNotifyIconBlazorApp.Data;

public static class WebProgram
{
    /// 
    /// 非常普通的一个 .NET 6 WebHost
    /// 只添加了一个 ApplicationStarted 启动浏览器的操作
    /// 
    public static void RunWebHost(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddRazorPages();
        builder.Services.AddServerSideBlazor();
        builder.Services.AddScoped();

        var app = builder.Build();

        if (!app.Environment.IsDevelopment())
        {
            app.UseExceptionHandler("/Error");
        }

        app.UseStaticFiles();

        app.UseRouting();

        app.MapBlazorHub();
        app.MapFallbackToPage("/_Host");

#if DEBUG
        app.Run();
#else
        // 启动 web host 并 打开浏览器
        app.RunAndStartedOpenBrowser();
#endif
    }
}
NotifyIconUtils.cs

使用到了 appicon.ico 作为 托盘的图标 (必须有图标)
可以改为自己的 ico, 记住要改复制到输出目录 -> 始终复制
否则发布后,找不到文件

using System.Runtime.InteropServices;
using System.Diagnostics;

public static class NotifyIconUtils
{
    #region 句柄实用工具

    [DllImport("User32.dll", EntryPoint = "FindWindow")]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", EntryPoint = "GetSystemMenu")]
    static extern IntPtr GetSystemMenu(IntPtr hWnd, IntPtr bRevert);

    [DllImport("user32.dll", EntryPoint = "RemoveMenu")]
    static extern IntPtr RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);

    [DllImport("user32.dll")]
    static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);

    [DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
    public static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    /// 
    /// 控制台窗口句柄
    /// 
    public static IntPtr ConsoleWindowHandle { get; set; }

    /// 
    /// 自动寻找当前控制台句柄
    /// 
    /// 
    public static IntPtr GetConsoleWindowHandle()
    {
        if (ConsoleWindowHandle == default)
        {
            ConsoleWindowHandle = FindWindow(null, Console.Title);
        }

        return ConsoleWindowHandle;
    }

    /// 
    /// 禁用控制台窗口的关闭按钮
    /// 
    public static void DisableCloseButton()
    {
        IntPtr closeMenu = GetSystemMenu(GetConsoleWindowHandle(), IntPtr.Zero);
        uint SC_CLOSE = 0xF060;
        RemoveMenu(closeMenu, SC_CLOSE, 0x0);
    }

    static void HiddenConsoleWindow()
    {
        uint SW_HIDE = 0;
        ShowWindow(GetConsoleWindowHandle(), SW_HIDE);
    }

    static void ShowConsoleWindow()
    {
        uint SW_SHOW = 5;
        ShowWindow(GetConsoleWindowHandle(), SW_SHOW);
        SetForegroundWindow(GetConsoleWindowHandle());
    }

    static bool _visable = true;

    /// 
    /// 通过属性控制 console 的显示与隐藏
    /// 
    public static bool ConsoleVisable
    {
        get { return _visable; }
        set
        {
            _visable = value;
            if (_visable)
                ShowConsoleWindow();
            else
                HiddenConsoleWindow();
        }
    }

    /// 
    /// 关闭某一个窗口
    /// 
    public static void CloseWindow(IntPtr hwnd)
    {
        UInt32 WM_CLOSE = 0x0010;
        SendMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    }

    #endregion

    #region 托盘图标

    private static NotifyIcon _notifyIcon = new();

    /// 
    /// 初始化
    /// 在程序的开头执行
    /// 
    public static void Init()
    {
        NotifyIconInitialization();
        ShowNotifyIcon();
        DisableCloseButton();
        ConsoleVisable = false;
    }

    public static void NotifyIconInitialization()
    {
        _notifyIcon.Icon = new Icon("appicon.ico");
        _notifyIcon.Visible = false;
        _notifyIcon.Text = Console.Title;

        var menu = new ContextMenuStrip()
        {
            RenderMode = ToolStripRenderMode.System,
        };

        menu.Items.Add(new ToolStripMenuItem()
        {
            Name = "OpenUrl",
            Text = "打开浏览器"
        });

        menu.Items.Add(new ToolStripMenuItem()
        {
            Name = "Console",
            Text = "打开/关闭控制台"
        });

        menu.Items.Add(new ToolStripMenuItem()
        {
            Name = "Exit",
            Text = "退出"
        });

        // 设置右键菜单
        _notifyIcon.ContextMenuStrip = menu;

        menu.ItemClicked += (s, e) =>
        {
            Console.WriteLine("托盘菜单被点击");
            Console.WriteLine("被点击的菜单是:{0}", e.ClickedItem.Text);
            switch (e.ClickedItem.Name)
            {
                case "Exit":

                    // 结束程序
                    Application.Exit();
                    break;
                case "OpenUrl":
                    // 打开浏览器
                    OpenBrowser(Url);
                    break;
                case "Console":
                    // 控制台窗口的显示/隐藏
                    ConsoleVisable = !ConsoleVisable;
                    break;
            }
        };

        // 双击
        _notifyIcon.MouseDoubleClick += (s, e) =>
        {
            Console.WriteLine("托盘被双击.");
            ConsoleVisable = !ConsoleVisable;
        };
    }

    public static void ShowNotifyIcon()
    {
        _notifyIcon.Visible = true;
        // 弹出系统提示
        //_notifyIcon.ShowBalloonTip(3000, "", "我是托盘图标,右键单击显示菜单,左键双击 显示/隐藏 控制台窗口。", ToolTipIcon.None);
    }

    public static void HideNotifyIcon()
    {
        _notifyIcon.Visible = false;
    }

    #endregion

    public static string? Url { get; set; }

    /// 
    /// 打开浏览器
    /// 
    public static void OpenBrowser(string url)
    {
        Console.WriteLine("打开浏览器:" + url);
        var browser =
            RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new ProcessStartInfo("cmd", $"/c start {url}") :
            RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? new ProcessStartInfo("open", url) :
            new ProcessStartInfo("xdg-open", url); //linux, unix-like

        Process.Start(browser);
    }

    /// 
    /// 启动 web host 并 打开浏览器
    /// 
    /// 
    /// 
    public static void RunAndStartedOpenBrowser(this WebApplication app, string? url = null)
    {
        url ??= app.Services.GetService()?["urls"]?.Split(";")?[^1] ?? "http://localhost:5000";

        Url = url;

        // 从容器中拿到 生命周期对象
        var lifetime = app.Services.GetService();
        // 注册 程序启动成功后,要执行的方法
        lifetime?.ApplicationStarted.Register(() => { OpenBrowser(Url); });

        app.Run(url);
    }
}
目录结构

高亮的文件

Github

完整代码与例子, 可直接启动查看效果

https://github.com/xxxxue/MyNotifyIconApp

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/904579.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号