【问题标题】:delay Application Close best practice?延迟应用程序关闭最佳实践?
【发布时间】:2010-01-19 19:17:02
【问题描述】:

在用户选择退出 WinForms 程序后,有没有更好的方法来处理做某事的任务:

[edit 1 : 回应 'NoBugz 的评论] 在这种情况下,表单上没有 ControlBox,并且有理由放置一层当用户选择关闭表单时发生的事情的间接性[/edit 1]

[edit 2 : 响应所有 cmets 截至 GMT +7 18:35 January 20 ] 也许使用淡出 MainForm 是一个简单的说明在关闭应用程序时可能想要这样做:用户无法与之交互:这与用户终止应用程序的决定不言而喻。 [/编辑 2]

(使用某种形式的线程?)(对多线程应用程序的影响?)(这段代码“味道不好”吗?)

    // 'shutDown is an external form-scoped boolean variable
    //
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        // make sure we don't block Windows ShutDown
        // or other valid reasons to close the Form
        if (e.CloseReason != CloseReason.ApplicationExitCall) return;

        // test for 'shutDown flag set here
        if (shutDown) return;

        // cancel closing the Form this time through
        e.Cancel = true;

        // user choice : default = 'Cancel
        if (MessageBox.Show("Exit Application ?", "", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == System.Windows.Forms.DialogResult.OK)
        {
            // user says Exit : activate Timer, set the flag to detect Exit
            timer1.Enabled = true;
            shutDown = true;
        }
    }

总结:在一个非常标准的 WinForms 应用程序中(一个 MainForm 以标准方式在 Program.cs 中启动):在 MainForm 的 FormClosing 事件处理程序中:

  1. 立即退出(触发默认行为:关闭 MainForm 并退出应用程序)如果:

    一个。 CloseReason 是任何其他 CloseReason.ApplicationExitCall

    b.如果一个特殊的布尔变量设置为 true,或者

  2. 如果没有立即退出:取消对 FormClosing 的“第一次调用”。

  3. 然后用户通过 MessageBox.Show 对话框做出选择,退出应用程序或取消:

    一个。如果用户取消,当然,应用程序保持“原样”。

    b.如果用户选择了“退出:

    1. 将特殊的布尔标志变量设置为真

    2. 运行一个 Timer 来做一些特殊的事情。

    3. 当 Timer 代码中的内部测试检测到“特殊内容”已完成时,它会调用 Application.Exit

【问题讨论】:

  • @nobugz 感谢您的评论;在这种情况下,我省略了说Form上没有ControlBox。我将在其中进行编辑。
  • @nobugz 感谢您的 cmets !这是根据客户的要求设计的,有点不标准的 WinForms UI;在关闭应用程序时通过计时器的“滴答”事件执行某些代码是有原因的:在视觉上,事情确实发生了,但是当应用程序被关闭时,最终用户无法与之交互。为了关闭应用程序而添加一个必需的验证步骤是“规范”的一部分。

标签: c# winforms formclosing


【解决方案1】:

我作为开发者和用户的建议:

一个非常快的任务

  • 只需在 Closing 事件处理程序中执行任务即可。

一个不太快,但不是非常慢的任务

  • 使用 Closing 事件处理程序中的任务创建一个非后台线程(因此当应用程序退出时它不会关闭)。
  • 让应用程序退出。表单将消失,等等,但该过程将继续运行,直到任务完成。
  • 请记住在该工作线程中处理异常等。并确保如果用户在该任务完成之前重新打开您的应用程序,事情不会崩溃。

较慢的任务

  • 在 Closing 事件处理程序中,打开一个正在关闭的表单并让表单本身关闭。
  • 在关闭表单中/后面执行任务,同时显示一些友好的进度和信息。
  • 任务完成后退出应用程序。

一些未经测试的示例代码。我们正在我们的一个应用程序中做类似的事情。在我们的案例中,任务是将窗口属性(位置、大小、窗口状态等)存储到数据库中。

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    // If it wasn't the user who asked for the closing, we just close
    if (e.CloseReason != CloseReason.UserClosing)
        return;

    // If it was the user, we want to make sure he didn't do it by accident
    DialogResult r = MessageBox.Show("Are you sure you want this?", 
                                     "Application is shutting down.",
                                     MessageBoxButtons.YesNo, 
                                     MessageBoxIcon.Question);
    if (r != DialogResult.Yes)
    {
        e.Cancel = true;
        return;
    }
}

protected override void OnFormClosed(FormClosedEventArgs e)
{
    // Start the task
    var thread = new Thread(DoTask)
    {
        IsBackground = false,
        Name = "Closing thread.",
    };
    thread.Start();

    base.OnFormClosed(e);
}

private void DoTask()
{
    // Some task to do here
}

【讨论】:

  • +1 感谢您的评论!真的很喜欢你按时间消耗制定策略的方式。关于防止重新打开的非常好的一点(将考虑使应用程序成为单例)。在这种情况下,当 MainForm/Application 关闭时,UI 会产生副作用,并且要求用户确认关闭的“额外步骤”是必需的。但发生的事情确实属于“快速”类别。
  • @BillW:除非确实有必要,否则不要阻止重新打开。作为用户,我几乎没有什么比关闭应用程序更讨厌的了,然后在尝试再次启动它时,我收到一条消息,表明它已经在运行。如何处理这个问题很大程度上取决于你的任务是什么。 (将在我的答案中添加一些示例代码,您可以在结束事件中执行哪些操作)
  • 感谢 Svish,提供示例代码!我很好奇覆盖 FormClosed 事件并在那里启动“终结器”线程而不是在 Form 的“默认”FormClosed 事件中的优势是什么。
  • @BillW:哦,那只是因为我的代码中的这 3 个方法实际上分布在三个类中。 MainForm_FormClosing 事件处理程序位于实际的 MainForm 中。 OnFormClosed 位于 MainForm 继承自的不同类中,而 DoTask 方法位于完全不同的类中。在我们的应用程序中发生的情况是,当一个表单继承自那个特殊表单时,它会在关闭时自动保存表单的位置、大小和状态。覆盖的原因只是因为我认为在处理表单继承时推荐它。我认为:p
  • 有没有办法使用任务(来自 System.Threading.Tasks)实现类似的结果(进程一直运行直到工作完成)?
【解决方案2】:

我看不出有什么“错误”。我唯一的建议是让用户知道程序正在“关闭”,方法是在系统托盘上方打开一个非模式窗口或通知烤面包机。

【讨论】:

  • +1 感谢您的评论!我喜欢系统托盘通知的想法,并将对此进行探索。
【解决方案3】:

几个小问题:

  • 我会质疑计时器的必要性:由于应用程序无论如何都会退出,因此用户不会期望 UI 响应,为什么不简单地在 UI 线程上执行清理代码?正如@Dave Swersky 所建议的,清理期间的小通知窗口会很礼貌。

  • 如果应用程序退出由 Windows 关闭或用户注销触发,会发生什么情况?

【讨论】:

  • 感谢您的评论!在这种情况下需要 Timer,并且应用程序在 Timer 业务完成之前不会退出。在 Timer 'Tick 事件中执行的代码所做的事情并不是用户可以与之交互的东西,按照设计:应用程序关闭过程存在一些“视觉副作用”,但同样,用户只能“看到”他们,而不是与他们互动。
猜你喜欢
  • 1970-01-01
  • 2015-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多