65.9K
CodeProject 正在变化。 阅读更多。
Home

有用的 WPF 线程扩展方法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (2投票s)

2009年6月17日

CPOL

1分钟阅读

viewsIcon

28045

有用的 WPF 线程扩展方法

如果您正在使用 WinForms 或 WPF,您很可能会遇到一些需要在新线程中运行的耗时操作。 初学者可能会尝试创建新的线程,这也可以,但这意味着您需要负责新线程的整个生命周期,这会变得很棘手。

更好的方法是使用 ThreadPool 或使用 BackgroundWorker 组件,该组件在底层使用 ThreadPool

然而,即使使用这些方法,最重要的规则是控件由一个线程拥有,即创建控件的线程。 通常是 UI 线程。 因此,当您尝试从后台线程更新控件时,会遇到问题。

这段代码演示了跨线程调用的问题

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Windows;
   6:  using System.Windows.Controls;
   7:  using System.Windows.Data;
   8:  using System.Windows.Documents;
   9:  using System.Windows.Input;
  10:  using System.Windows.Media;
  11:  using System.Windows.Media.Imaging;
  12:  using System.Windows.Navigation;
  13:  using System.Windows.Shapes;
  14:  using System.ComponentModel;
  15:  using System.Windows.Threading;
  16:  
  17:  namespace BackgroundThread
  18:  {
  19:  
  20:  
  21:      public partial class Window1 : Window
  22:      {
  23:          private Int32 currentCount = 0;
  24:          private Int32 maxCount = 500;
  25:          private float factor = 0;
  26:  
  27:          public Window1()
  28:          {
  29:              InitializeComponent();
  30:  
  31:          }
  32:  
  33:          private void btnGo_Click(object sender, RoutedEventArgs e)
  34:          {
  35:              factor = (float)100 / maxCount;
  36:  
  37:              BackgroundWorker bgWorker = new BackgroundWorker();
  38:              bgWorker.WorkerReportsProgress = true;
  39:              bgWorker.WorkerSupportsCancellation = false;
  40:  
  41:              //DoWork
  42:              bgWorker.DoWork += (s2, e2) =>
  43:              {
  44:                  for (currentCount = 0;
  45:                      currentCount < maxCount; currentCount++)
  46:                  {
  47:                      lstItems.Items.Add(
  48:                          String.Format(“Count {0}”, currentCount));
  49:                  }
  50:              };
  51:  
  52:              //ProgressChanged
  53:              bgWorker.ProgressChanged += (s3, e3) =>
  54:              {
  55:                  pgbar.Value = e3.ProgressPercentage;
  56:              };
  57:  
  58:              bgWorker.RunWorkerAsync();
  59:  
  60:          }
  61:      }
  62:  } 

运行后将产生以下结果

37314/crossthread-thumb.jpg

那么我们如何解决这个问题呢? 我们可以使用 Dispatcher.Invoke 围绕违规项目,但也许更优雅的解决方案是使用扩展方法。

 1:  public static class WPFThreadingExtensions
 2:  {
 3:      /// <summary>
 4:      /// Simple helper extension method to marshall to correct
 5:      /// thread if its required
 6:      /// </summary>
 7:      /// <param name="""control""">The source control</param>
 8:      /// <param name="""methodcall""">The method to call</param>
 9:      /// <param name="""priorityForCall""">The thread priority</param>
10:      public static void InvokeIfRequired(
11:          this DispatcherObject control,
12:          Action methodcall,
13:          DispatcherPriority priorityForCall)
14:      {
15:          //see if we need to Invoke call to Dispatcher thread
16:          if (control.Dispatcher.Thread != Thread.CurrentThread)
17:              control.Dispatcher.Invoke(priorityForCall, methodcall);
18:          else
19:              methodcall();
20:      }
21:  }

然后我们可以在代码中像下面这样简单地使用它

   1:      factor = (float)100 / maxCount;
   2:  
   3:      BackgroundWorker bgWorker = new BackgroundWorker();
   4:      bgWorker.WorkerReportsProgress = true;
   5:      bgWorker.WorkerSupportsCancellation = false;
   6:  
   7:      //DoWork
   8:      bgWorker.DoWork += (s2, e2) =>
   9:      {
  10:          for (currentCount = 0;
  11:              currentCount < maxCount; currentCount++)
  12:          {
  13:  
  14:              this.InvokeIfRequired(() =>
  15:              {
  16:                  lstItems.Items.Add(
  17:                      String.Format(“Count {0}”, currentCount));
  18:              },
  19:                  DispatcherPriority.Background);
  20:  
  21:              bgWorker.ReportProgress((int)(factor * (currentCount + 1)));
  22:  
  23:          }
  24:      };
  25:  
  26:      //ProgressChanged
  27:      bgWorker.ProgressChanged += (s3, e3) =>
  28:      {
  29:          this.InvokeIfRequired(() =>
  30:          {
  31:              pgbar.Value = e3.ProgressPercentage;
  32:          },
  33:          DispatcherPriority.Background);
  34:  
  35:  
  36:      };
  37:  
  38:      bgWorker.RunWorkerAsync();
  39:  
  40:  }

运行后,允许将跨线程调用编排到正确的 Dispatcher 对象。

37314/better-thumb.jpg

希望这有帮助!

© . All rights reserved.