代码之家  ›  专栏  ›  技术社区  ›  matif

如何更改NotifyIcon。通过命令行参数运行WinForm中的文本?

  •  1
  • matif  · 技术社区  · 7 年前

    但我发现,当我在运行过程中使用参数调用它时,我想要改变的是 null ( NotifyIcon() MenuItem() ),当我使用参数时,它似乎运行了不同的应用程序。我也试过了 Invoke() 但在这本书中没有这个定义 NotifyIcon() .

    这是我写的代码:

    static void Main(string[] args)
    {
        if (args.Length > 0)
        {
            Arg_Call(args[0]);
        }
        if (new Mutex(true, "{XXX}").WaitOne(TimeSpan.Zero, true))
        {
            Init_Tray();
            Application.Run();
        }
    }
    private static NotifyIcon trayicon;
    
    private static void Init_Tray()
    {
        trayicon = new NotifyIcon() { Icon = new Icon(@"D:\projects\Icon.ico"), Text = "Waiting", Visible = true };
        trayicon.Visible = true;
        Application.Run();
    }
    private static void Arg_Call(string args)
    {
        trayicon.Invoke((MethodInvoker)delegate {
            trayicon.Text = "OK";
        }); //from: https://stackoverflow.com/a/661662/8199423
    }
    

    我哪里错了?如何以及改变 NotifyIcon.Text

    1 回复  |  直到 7 年前
        1
  •  4
  •   Peter Duniho    7 年前

    很抱歉,我无法充分解释为什么您的问题与现有的“单实例应用程序”问题重复。我将在这里重申一下思路:

    1. “如何以及通过命令行参数更改运行表单中的文本的最佳方式是什么?”
    2. 您的需求涉及一个当前正在运行的流程,该流程表示 NotifyIcon 并希望使用命令行修改当前正在运行的进程的状态。
    3. 不同于 已经在运行的进程,该进程正在呈现 在任务托盘中。

    综上所述,我们得出的结论是,您希望在命令行上启动一个新流程,以与现有流程交互。实现这一目标的最简单方法是使用.NET中内置的单实例应用程序支持。这是因为对单实例应用程序的支持包括将新的命令行参数自动传递给以前运行的程序。因此,重复。

    综上所述,我的印象是,您仍然难以确定如何将这些信息应用到您的特定场景中。所以,也许这是一个机会来说明我所信奉的哲学的有效性,通过向你们展示你们看似不同的问题实际上是我声称的问题

    那么,让我们从最初的场景开始。我没有使用你发布的代码,因为大部分代码都是不需要的。对我来说,从头开始似乎更简单。为此,我写了一点 TrayManager 部分功能:

    class TrayManager : IDisposable
    {
        private readonly NotifyIcon _notifyIcon;
    
        public TrayManager()
        {
            _notifyIcon = new NotifyIcon
            {
                ContextMenu = new ContextMenu(new[]
                {
                    new MenuItem("Exit", ContextMenu_Exit)
                }),
                Icon = Resources.TrayIcon,
                Text = "Initial value",
                Visible = true
            };
        }
    
        public void Dispose()
        {
            Dispose(true);
        }
    
        public void SetToolTipText(string text)
        {
            _notifyIcon.Text = text;
        }
    
        protected virtual void Dispose(bool disposing)
        {
            _notifyIcon.Visible = false;
        }
    
        private void ContextMenu_Exit(object sender, EventArgs e)
        {
            Application.ExitThread();
        }
    
        ~TrayManager()
        {
            Dispose(false);
        }
    }
    

    上面硬编码了图标的上下文菜单。当然,这是一个真实的程序,您可能希望将菜单与上述类解耦,以获得更大的灵活性。

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
    
            using (TrayManager trayManager = new TrayManager())
            {
                Application.Run();
            }
        }
    }
    

    那么,我们如何修改上述内容,以便在再次运行程序时,可以更改 Text 托盘图标 使用您键入的命令行参数?这就是单实例应用程序的用武之地。正如我之前标记的副本所示, What is the correct way to create a single-instance application? Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase

    一个小小的退步是,这个类是为Winforms程序设计的,假设将有一个主窗体 Form 例子对于不需要实际表单的程序,这意味着创建一个 从未显示的实例,确保从未显示它确实需要一些欺骗。明确地:

    class TrayOnlyApplication : WindowsFormsApplicationBase
    {
        public TrayOnlyApplication()
        {
            IsSingleInstance = true;
            MainForm = new Form { ShowInTaskbar = false, WindowState = FormWindowState.Minimized };
    
            // Default behavior for single-instance is to activate main form
            // of original instance when second instance is run, which will show
            // the window (i.e. reset Visible to true) and restore the window
            // (i.e. reset WindowState to Normal). For a tray-only program,
            // we need to force the dummy window to stay invisible.
            MainForm.VisibleChanged += (s, e) => MainForm.Visible = false;
            MainForm.Resize += (s, e) => MainForm.WindowState = FormWindowState.Minimized;
        }
    }
    

    上述内容中唯一能为我们提供所需的单实例应用程序行为的是 IsSingleInstance = true; .其他一切都是为了满足以下要求: 一些 MainForm ,而不在屏幕上实际显示该对象。

    Program

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
    
            using (TrayManager trayManager = new TrayManager())
            {
                TrayOnlyApplication app = new TrayOnlyApplication();
    
                app.StartupNextInstance += (s, e) => trayManager
                    .SetToolTipText(e.CommandLine.Count > 0 ? e.CommandLine[0] : "<no value given>");
                app.Run(args);
            }
        }
    }
    

    您将注意到两个变化:

    1. 除了 托盘管理器 ,它处理 托盘图标 ,我们现在还创建 TrayOnlyApplication 对象,订阅其 StartupNextInstance 文本 的属性 托盘图标 对象(通过将其传递给专门为此目的创建的方法)。
    2. Application.Run() Run() 我们的方法 类继承自 WindowsFormsApplicationBase Application.ExitThread() 方法,因此消息泵送的两种方法都与 托盘管理器 .

    Windows窗体应用程序库 提供了一种机制来避免这种情况 Startup StartupNextInstance公司 启动 事件仅在以下情况下引发 其他实例已在运行。一、 e.在你真正想做的事情中,比如显示托盘图标。

    我们可以利用该事件延迟创建

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
    
            TrayManager trayManager = null;
            TrayOnlyApplication app = new TrayOnlyApplication();
    
            // Startup is raised only when no other instance of the
            // program is already running.
            app.Startup += (s, e) => trayManager = new TrayManager();
    
            // StartNextInstance is run when the program if a
            // previously -run instance is still running.
            app.StartupNextInstance += (s, e) => trayManager
                .SetToolTipText(e.CommandLine.Count > 0 ? e.CommandLine[0] : "<no value given>");
    
            try
            {
                app.Run(args);
            }
            finally
            {
                trayManager?.Dispose();
            }
        }
    }
    

    注意,在这里,我们需要编写 try / finally 显式而不是使用 using 语句需要在声明变量时对其进行初始化,我们希望将初始化推迟到以后,或者永远不要进行,这取决于正在运行的实例。

    (不幸的是,没有办法推迟在 纸盘应用 运行() 方法,该方法需要有效的 对象,并在该调用中确定正在运行的实例,而不是在此之前。)

    当然, 任何 Windows窗体应用程序库 类提供了预先制作的功能。做同样的事情还有很多其他方法,每种方法都有各自的优缺点。