gpt4 book ai didi

c# - 如何通过命令行参数在运行的WinForm中更改NotifyIcon.Text?

转载 作者:太空宇宙 更新时间:2023-11-03 22:57:06 24 4
gpt4 key购买 nike

这是仅具有托盘图标的Windows窗体应用程序。我试图使用参数来控制某些内容,并更改表单上用于显示状态信息的文本。

但是我发现,当我在运行期间使用参数调用它时,我想更改的内容是nullNotifyIcon()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属性的最佳方法是什么?

最佳答案

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


您写道:“如何通过命令行参数更改正在运行的表单中文本的最佳方法是什么?”
您的需求涉及一个当前正在运行的进程,该进程在任务栏中显示NotifyIcon,并且希望使用命令行来修改该当前正在运行的进程的状态。
很简单的事实是,当您在命令行上键入任何内容时,它将启动一个全新的过程。该过程必然不同于已经运行的过程,该过程正在任务栏中显示NotifyIcon


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

正如我之前提到的,您应该尝试发展概括性的技巧,并看看新问题看起来真的只是变相的旧问题,或者您或其他人已经知道如何解决。

就像所有问题解决都可以概括为“将大问题分解为小问题,根据需要重复进行,直到所有小问题都是您已经知道如何解决的问题”一样,编程通常不是解决问题的方法新问题,而不是意识到您当前的问题确实是您已经知道如何解决的问题。

综上所述,我给您的印象是,您仍然很难确定如何将这些信息应用于您的特定方案。因此,也许这是一个机会,通过向您展示您看似不同的问题实际上就是我所声称的问题,来说明我所主张的哲学的有效性。 :)

因此,让我们从原始场景开始。我没有使用您发布的代码,因为大部分不需要的代码。从头开始似乎更简单。为此,我编写了一个小的TrayManager类,该类封装了功能的实际NotifyIcon部分:

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();
}
}
}


因此,我们如何修改上述内容,以便在再次运行该程序时,可以使用键入的命令行参数来更改 TextNotifyIcon属性?那就是单实例应用程序出现的地方。正如我在前面标记的副本 What is the correct way to create a single-instance application?中所看到的那样,实现此目的的最简单方法之一就是使用 Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase类,该类已经建立了对单实例的支持。应用程序和一种将新的命令行参数传递给现有流程的机制。

一个小缺点是该类是为Winforms程序设计的,并假定将有一个主要形式。要使用它,需要创建一个 Form实例。对于不需要实际形式的程序,这意味着创建一个从未显示过的 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;的设置。此处的其他所有内容只是为了满足某些 Form对象以 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);
}
}
}


您会注意到两个变化:


除了处理 TrayManagerNotifyIcon,我们现在还创建 TrayOnlyApplication对象,订阅它的 StartupNextInstance事件,以便我们可以接收给任何新实例提供的命令行参数,并使用该对象设置 Text对象的 NotifyIcon属性(通过将其传递给专门为此目的创建的方法)。
我们没有使用 Application.Run()来运行require message-pump循环来处理窗口消息,而是使用了从 Run()类继承的 TrayOnlyApplication类的 WindowsFormsApplicationBase方法。这两种方法都可以在程序运行时处理消息泵送,并在调用 Application.ExitThread()方法时将控制权返回给调用者,因此,两种消息泵送方法都可以使用 TrayManager中的代码进行。


现在,上面的示例只是对原始版本的轻微修改,该版本未强制执行单实例操作。您可能会注意到,无论它是否是第一个运行的实例,它总是会创建任务栏图标,这一点存在争议。随后的实例将运行,创建任务栏图标,然后立即将其关闭并退出。

WindowsFormsApplicationBase事件提供了一种避免这种情况的机制。当实例正在运行时,在运行的应用程序的任何实例中引发 Startup事件时,仅当没有其他实例在运行时,才会引发 StartupNextInstance事件。即在您实际要执行的操作中,例如显示任务栏图标。

我们可以利用该事件来推迟 Startup的创建,直到我们知道是否实际需要它为止:

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();
}
}
}


请注意,在这里,我们需要显式地编写 NotifyIcon / try而不是使用 finally语句,因为 using语句要求在声明变量时对其进行初始化,并且我们希望将初始化推迟到以后,或永远不会,取决于运行哪个实例。

(不幸的是,没有方法可以推迟在 using类中创建虚拟窗口,因为只需要调用 TrayOnlyApplication方法即可,该方法要求已经设置了有效的 Run()对象,并确定实例正在该调用中运行,而不是之前。)

这就是全部。显然,我希望上述内容准确地显示了您可以使用的单实例应用程序技术如何直接解决您寻求帮助的问题。通过为新运行的程序实例提供一种机制,以将传递给它的命令行参数传递给同一程序的已经运行的实例,该新运行的实例可以导致已经运行的实例执行任何工作它需要(例如,更改任务栏图标的工具提示文本)。

自然,任何类似的机制都将达到相同的结果。唯一重要的是让新运行的实例检测到现有实例,并与之通信。恰巧 Form类提供了预制的功能。有很多其他方法可以做相同的事情,每种方法各有利弊。

关于c# - 如何通过命令行参数在运行的WinForm中更改NotifyIcon.Text?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44876741/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com