【问题标题】:ProgressBar disappears when changed from within a thread从线程内更改时,ProgressBar 消失
【发布时间】:2011-11-08 17:15:43
【问题描述】:

为什么从线程中更改样式时进度条会消失?如何避免这种情况发生?

我有一个运行时间不确定(但可能很长)的线程,所以我想使用进度条来跟踪线程的进度。我在启动线程开始之前将进度条样式设置为选取框,但是一旦线程完成并将进度条样式设置回块(您可以通过设置 Form.CheckForIllegalCrossThreadCalls = false 从另一个线程执行此操作),进度条将从视觉上的形式。此外,如果我在表单上放置一个按钮来来回更改进度条样式,它将重新出现,并随着按钮的连续按下来回更改样式。

【问题讨论】:

  • 感谢所有快速回答。我不知道会那么糟糕。当我第一次尝试这样做时,msdn 说“你不能从那个线程做,也许你应该设置 Form.CheckForIllegalCrossThreadCalls = false”,所以我做了,这导致了这个
  • 一些 Msdn 文档可能有点误导,但这就是 StackOverflow 之类的地方的用途:)

标签: c# winforms multithreading progress-bar


【解决方案1】:

来自 MSDN 对 CheckForIllegalCrossThreadCalls 属性的评论:

当控件的创建线程以外的线程试图 访问该控件的方法或属性之一,通常会导致 不可预知的结果。一个常见的无效线程活动是调用 访问控件的 Handle 属性的错误线程。放 CheckForIllegalCrossThreadCalls 为 true 以查找和诊断此问题 调试时更轻松地进行线程活动。

从 UI 线程以外的线程访问控件通常被认为是不好的做法,通常是不允许的。

您应该考虑使用Control.Invoke 方法。我通常像这样处理跨线程 UI 更新:

public void UpdateProgressBar(int NewValue) 
{
   if(InvokeRequired)
      Invoke(() => UpdateProgressBar(NewValue));
   else
      progressBar.Value = NewValue;
}

这会将工作转移到 UI 线程上,在那里可以安全地访问控件。

【讨论】:

    【解决方案2】:

    (您可以通过设置 Form.CheckForIllegalCrossThreadCalls = false 从另一个线程执行此操作)

    不好。坏坏坏。 Baaaaaaaad。它们被称为非法跨线程调用是有原因的。

    您应该更新 UI 的唯一线程是 UI 线程。没有例外。为此,您应该使用BackgroundWorker 并调用ReportProgress 方法(并使用ProgressChangedEventHandler 处理它,这将保证在UI 线程上执行)。

    如果您没有使用BackgroundWorker,您可以传递一个委托,其中包含将进度条更新到进度条上的Invoke 方法的逻辑,以确保代码在UI 线程上运行。

    【讨论】:

      【解决方案3】:

      老实说,我以前从未听过Form.CheckForIllegalCrossTHreadCalls,但文档说:

      当控件的创建线程以外的线程试图 访问该控件的方法或属性之一,通常会导致 不可预知的结果。一个常见的无效线程活动是调用 访问控件的 Handle 属性的错误线程。

      将 CheckForIllegalCrossThreadCalls 设置为 true 以查找和诊断此问题 调试时更轻松地进行线程活动。

      您将其设置为 false 只需对您提出的问题说“闭嘴”。

      不能从除 UI 之外的线程更改控制。所以不要使用这个属性。 只需使用 Form 的 Invoke 在主线程上运行代码。

      SO Example

      【讨论】:

        【解决方案4】:

        哇!远离 CheckForIllegalCrossThreadClass。那个属性,虽然微软提供了它,而且我确信它有一个非常、非常、非常罕见的用途(我的意思是罕见),但永远不要碰它。它确实会给您的软件带来伤害。

        您可以使用简单的 ISyncronizeInvoke 方法来更新您的 UI,which I also wrote a blog post on(非常简单)。

        /// <summary>
        /// Helper class that allows synchronized invoking to be performed in a single line of code.
        /// </summary>
        internal static class SynchronizedInvoke {
            /// <summary>
            /// Invokes the specified action on the thread that the specified sync object was created on.
            /// </summary>
            public static void Invoke(ISynchronizeInvoke sync, Action action) {
                if (!sync.InvokeRequired) {
                    action();
                }
                else {
                    object[] args = new object[] { };
                    sync.Invoke(action, args);
                }
            }
        }
        

        使用简单。

        private void MyThreadWork() {
            // Do some work
            SynchnronizeInvoke.Invoke(uxProgressBar, () => uxProgressBar.Style = ProgressBarStyles.Blocks);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-05-05
          • 1970-01-01
          • 1970-01-01
          • 2018-05-18
          • 2012-09-08
          • 1970-01-01
          • 2012-04-16
          相关资源
          最近更新 更多