【问题标题】:Navigate to Another Form from System Tray Winforms从系统托盘 Winforms 导航到另一个表单
【发布时间】:2021-11-10 00:50:21
【问题描述】:

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

这就是我的目的

程序.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;
    }

但是,当我这样做时,系统托盘图标消失并且无法转到指定页面。有什么想法或经验来实现这一点?

提前谢谢各位!

【问题讨论】:

  • 您需要SignIn 的实例还是在MainApp 显示后不再需要?
  • @Steeeve 我不需要登录实例,当用户登录时应该显示主应用程序,在最小化到托盘后,主应用程序应该显示登录用户的帐户
  • 然后我会将SignIn 表单与MainApp 完全分开并更改static Main() 以便它显示SignIn with ShowDialog 并且如果登录成功Application.Run(new MainApp())
  • @Steeeve 我需要从登录开始,然后进入 mainapp。怎么可能有两个主要的方法..?

标签: c# winforms system-tray


【解决方案1】:

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

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.csMain 方法中使用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());
}

其余代码乍一看还可以。

【讨论】:

  • 谢谢伙计,但是当我在没有任何凭据的情况下简单地关闭登录表单时,主应用程序随即出现。这是我的工作流程 => workflow.
  • 那是因为你更改了我在 program.cs 中的代码。它必须是我写的,否则它会继续没有身份验证。
  • SignIn.cs 中也不需要 notifyIcon 或 mainApp。我不确定button1_Click 应该做什么,如果您需要运行mainApp,请始终使用DialogResult = DialogResult.OK 而不是Close() 或Hide() 关闭SignIn。并删除SignIn_FormClosing
  • 但是,我想使用普通用户的凭据和管理员的 USB 进行身份验证
  • 将 'button1_Click` 中的 this.Close() 替换为 DialogResult = DialogResult.OK 并删除 this.Hide();(后者也会结束 ShowDialog 循环)。
【解决方案2】:

我有一些很久以前(十多年前)挖出来的代码。 Windows 10 稍微打破了它,所以最近有一些变化。您要做的一件事(如果您要取消关闭)是处理 WmQueryEndSessionWmEndSession 消息 - 否则您的应用将阻止系统关闭。

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

以下是一些工作代码的摘录。它只是从档案中复制/粘贴。我没有 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 的评论可能来自 Framework v1.x 时代;今天可能不是真的

forceExit 标志最初为假。当用户在按住控制键的情况下单击关闭按钮或从上下文菜单中选择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 选项(这就是恢复表单的方式)。此应用在通知区域(又名系统托盘)中发挥最大作用。恢复的时候,主要是用来修改app设置等。从cmets看,Win10出来后我需要在这里摆弄一下:

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

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

【讨论】:

  • 是的,伙计,我明白发生了什么,但是系统托盘图标仍然消失,我相信你已经接近它,但它没有按照我想要的方式工作......或者你可以向我解释所有代码的去向?
  • @opuscorpus:WndProc 代码是进入窗体(/window 的)Win32 WndProc 的标准 Windows 窗体方法。只需将其放在您的表单类中即可。 FormClosing 事件处理程序为表单处理该事件。我的托盘图标有一个包含多个条目的上下文菜单。其中三个是RestoreHideQuit。这些是相应的事件处理程序
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-23
  • 2016-11-15
  • 1970-01-01
  • 2012-02-16
  • 2020-05-21
  • 2021-01-09
相关资源
最近更新 更多