【问题标题】:C# Winforms How to update toolStrip in functionC# Winforms如何在函数中更新toolStrip
【发布时间】:2017-02-13 04:37:13
【问题描述】:

我正在为一个程序构建一个 UI,但我不明白为什么我的进度条在单击转换按钮后不可见。

private void convertButton_Click(object sender, EventArgs e)
{
    toolStripProgressBar.Visible = true;

    ...

    toolStripProgressBar.Visible = false;
}

我在 Python 中遇到了与 tkinter 类似的问题,我不得不调用一个函数来更新空闲任务。有没有办法在不使用线程的情况下使用 Windows 窗体来做到这一点?

编辑:附带说明,这是工具条中的进度条,其中还包含一个标签,该标签会随状态栏文本进行更新。有没有办法让左侧的标签和进度条在另一侧,而不是在左侧彼此相邻?

【问题讨论】:

    标签: c# winforms visible toolstrip


    【解决方案1】:

    嗯,一种不使用线程的方法 (Application.DoEvents),但我强烈建议您不要使用它。重入很讨厌,而且你真的不希望 UI 线程被捆绑。

    改用BackgroundWorker - 这很简单,而且几乎为进度条设计。它消除了使用单独线程并将进度报告回 UI 线程的麻烦。不需要Control.Invoke 等 - 它会为您处理。

    lots of tutorials for BackgroundWorker - 你应该不会花太长时间来使用它。

    【讨论】:

    • 我假设您建议将长时间运行的进程移至单独的线程,以保持 UI 始终正确可用?
    • 是的 - 这就是 BackgroundWorker 让工作变得简单的原因。
    • 感谢您的帮助。我希望避免使用线程,因为手动操作非常困难,而且这是一个简单的 UI,但这听起来很完美。谢谢。
    【解决方案2】:

    根据您提出的在没有线程的情况下执行此操作的方法的问题,即使用 Application.DoEvents(); 执行此操作。 (只需在将进度条设置为可见后立即添加该调用。)

    现在我同意 Jon Skeet 的观点,尽管 BackgroundWorker 是一种更好的方法,但它确实使用了单独的线程。

    【讨论】:

      【解决方案3】:

      您需要在独立于 UI 线程的线程中执行您的进程,然后让它定期向 UI 线程报告其进度。如果您的转换操作在 UI 线程内工作,它只会在操作完成之前无响应。

      【讨论】:

        【解决方案4】:

        只有在消息处理过程中允许绘制时,进度条才会变得可见。当您处于事件处理程序的中间时,消息处理通常不会发生。如果要显示进度条,则必须将可见性设置为 true,启动后台线程以完成工作并从处理程序返回。

        【讨论】:

          【解决方案5】:

          我猜问题是您代码中的"..." 是一个长时间运行的过程。 UI 更新不是即时的,而是必须通过 windows 中的消息队列运行,然后绘制到屏幕上。队列被抽出,并且绘制发生在与您的事件相同的线程中。

          因此,任何长时间运行的任务都需要移动到不同的线程。不仅如此,您的代码行需要在该线程终止后调用。否则,您设置进度条,然后立即再次将其关闭。

          一种方法是使用 BackgroundWorker 控件。

          【讨论】:

            【解决方案6】:

            这里有两个链接试图解释事情是如何工作的: (1)(2)

            现在,我会尽快解释。在 Windows 窗体应用程序中发生的大部分事情都发生在单个线程中,通常 Main() 运行在同一个线程中。如果打开 Program.cs,您会看到 Main() 有一行如下所示:

            Application.Run(new Form1());
            

            如果您在任何时候调试应用程序并检查调用堆栈,您会看到它将追溯到该 Run 方法。这意味着 Windows 窗体应用程序实际上是 Run 方法的连续运行。那么,Run在做什么呢? Run 正在吃一个消息队列,Windows 通过该消息队列向它发送消息。 Run 然后将这些消息发送到正确的控件,这些控件本身会执行添加与按下的键相对应的文本、重绘自身等操作。请注意,所有这些都发生在与单个线程一起运行的无限循环期间,因此您正在输入天气或者简单地移动窗口,这些消息的负载被传递到应用程序,应用程序反过来处理它们并做出相应的反应,所有这些都在单个线程中。控件还可以通过队列向自己发送消息,甚至您可以通过 Control.BeginInvoke 将消息放入泵中。这些控件所做的其中一件事是根据发生的情况引发事件。因此,如果您单击一个按钮,您为处理该单击而编写的代码最终会间接地由 Application.Run 方法运行。

            现在,您的代码正在发生的事情是,即使您将进度条的可见状态更改为可见,然后更新其值,您也将其可见性更改为 false,所有这些都使用相同的方法。这意味着只有在您离开该方法后,Application.Run() 才能继续迭代和消费消息队列,有效地要求进度条更新其显示。发生这种情况时,您已经将进度条的可见性设置为 false,这是您在退出方法之前所做的最后一件事。 DoEvents() 是一种快速而肮脏的解决方法,因为它读取队列中的消息并处理它们。使用它我感觉不太舒服,因为它会带来重入问题。

            使用线程是一个很好的解决方案,但我建议在这种情况下使用 ThreadPool 线程而不是自定义线程,因为我倾向于仅在我的长寿命线程数量有限并且我需要控制它们的生命周期。使用线程最简单、最实用的方法是使用 BackgroundWorker 组件,尽管如果您想真正了解发生了什么,我建议您通过了解如何使用委托进行 Windows 窗体多线程处理的痛苦。

            【讨论】:

            • 感谢您的分解。 BackgroundWorker 解决方案有效,我将在未来研究代表。
            【解决方案7】:

            我的解决方案是在状态条上调用刷新。
            我相信这会导致 UI 线程重新绘制状态条。

            toolStripStatusBar1.PerformStep();
            statusStrip1.Refresh();
            

            这适用于 .NET 4.0。尽管这个问题很老,但这是我在谷歌上搜索这个问题时发现的第一个问题。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-10-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-09-08
              • 1970-01-01
              • 2011-05-02
              • 1970-01-01
              相关资源
              最近更新 更多