从系统托盘 Winforms 导航到另一个表单

Navigate to Another Form from System Tray Winforms

我正在开发一个 WinForms 应用程序,我试图在关闭时将表单最小化到系统尝试,而当从系统托盘打开时我想直接转到主应用程序而无需再次登录。 重点是减少进入主应用程序的登录尝试,应用程序应 运行 在后台运行,直到帐户持有人退出应用程序并看到登录屏幕。

这就是我要做的

Program.cs

static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Splashscreen());
        Application.Run(new SignIn());                                       
    }

这是我从登录表单导航到主应用程序的方式

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        waitForm.Close();
        this.Hide();
        //Nothing to do with this system tray problem, just checking for another valid condition
        if(condition == true)
        {                 
            MainApp mainapp = new MainApp();
            mainapp.ShowDialog();                
        }            
        this.Close();
    }

登录后进入的主应用

private void MainApp_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (e.CloseReason == CloseReason.UserClosing)
        {
            notifyIcon1.Visible = true;
            notifyIcon1.ShowBalloonTip(500);
            this.Hide();
            e.Cancel = true;                
        }
    }

    private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
    {
        this.Show();
        notifyIcon1.Visible = false;
        WindowState = FormWindowState.Normal;
    }

但是,当我执行此操作时,系统托盘图标消失并且无法转到指定页面。在实现这一目标方面有什么想法或经验吗?

提前谢谢大家!

我有一些很久以前(十多年前)挖出来的代码。 Windows10有点破,所以最近有一些改动。您要做的一件事(如果您要取消关闭)是处理 WmQueryEndSessionWmEndSession 消息 - 否则您的应用程序将阻止系统关闭。

private const int WmQueryEndSession=0x11;
private const int WmEndSession=0x16;

这里是一些工作代码的摘录。它只是来自存档的 copy/paste。我没有 VS 系统,我现在可以测试这段代码。

这是结束会话代码:

//needed to get this thing to close cleanly.  OnSessionEnding and OnSessionEnded didn't work
protected override void WndProc(ref Message m){
    switch(m.Msg){
        case WmQueryEndSession:
            m.Result=(IntPtr) 1;        //basically "e.Cancel = false;"
            goto case WmEndSession;
        case WmEndSession:
            forceExit=true;
            Close();
            return;
        default:
            base.WndProc(ref m);
            break;
    }
}

请注意,关于 OnSessionEndingOnSessionEnded 的评论可能来自框架 v1.x 时代;今天可能不是这样

forceExit 标志最初为 false。当用户在按住控制键的情况下单击关闭按钮或从上下文菜单中选择 Quit 时,它会被设置。

private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
    if(forceExit)
        return; //close and quit
    //otherwise
    forceExit=false;
    if(!ModifierKeys.HasFlag(Keys.Control)) {
        WindowState=FormWindowState.Minimized;
        e.Cancel=true;
    }
 }

我的上下文菜单中有一个 Restore 选项(这就是恢复表单的方式)。此应用程序在 通知区域 (也称为 系统托盘 )中的用途最大。在恢复后的状态下,它主要用于更改应用程序设置等。根据评论,我需要在 Win10 出来后 fiddle 这里做一些事情:

private void RestoreCmd_Click(object sender, EventArgs e) {
    Show();
    //restoring from initially minimized on Windows 10 seems to completely mess up all the control positions and sizes
    //so store everything here and restore it below
    Dictionary<Control, (Point location, Size size)> controlPositions = null;
    if (textBox1.Size.Width != 0)
    {
        controlPositions = new Dictionary<Control, (Point pos, Size size)>();
        foreach (var ctrl in this.Controls.Cast<Control>())
        {
            controlPositions.Add(ctrl, (location: ctrl.Location, size:ctrl.Size));
        }
    }

    WindowState=FormWindowState.Normal;
    //restore all the control positions and sizes
    if (controlPositions != null)
    {
        foreach (var position in controlPositions)
        {
            position.Key.Size = position.Value.size;
            position.Key.Location = position.Value.location;
        }
    }
}

上下文菜单中有 HideQuit 个条目:

private void HideCmd_Click(object sender, EventArgs e) {
    WindowState=FormWindowState.Minimized;
}

private void QuitCmd_Click(object sender, EventArgs e) {
    forceExit=true;
    Close();
}

这个应用程序有很多功能。我想我已经提取了所有 show/hide 管理并将其放入此答案中。如果我遗漏了什么,请告诉我。

问题是,隐藏表单会结束 ShowDialog() 模态循环。因此,在 backgroundWorker1_RunWorkerCompleted 中,当 MainApp 表单被隐藏并退出 main 方法中的 Application.Run() 循环时,您的 SignIn 表单将关闭。在您现有的代码中查看我的评论:

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    waitForm.Close();
    this.Hide();
    //Nothing to do with this system tray problem, just checking for another valid condition
    if(condition == true)
    {
        MainApp mainapp = new MainApp();
        // this method will exit when mainapp will be hidden:
        mainapp.ShowDialog();
    }
    // this will exit your Application.Run(new SignIn()) in the static Main methods:
    this.Close();
}

如您所说,成功登录后不需要 SignIn 实例,您可以按如下方式更改您的应用程序。
SignIn 表单中更改如下方法:

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    waitForm.Close();
    //Nothing to do with this system tray problem, just checking for another valid condition
    DialogResult = condition ? DialogResult.OK : DialogResult.Cancel;
}

这样您就可以在 Program.cs 中的 Main 方法中使用 SignInDialogResult:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Splashscreen());
    using (var signIn = new SignIn())
    {
        switch (signIn.ShowDialog())
        {
            case DialogResult.OK:
                break;
            default:
                return;
        }
    }
    Application.Run(new MainApp());
}

其余代码乍一看还不错。