System.Windows.Threading.Dispatcher和WinForms?

时间:2008-11-19 19:49:47

标签: winforms multithreading dispatcher begininvoke

System.Windows.Threading.Dispatcher是否适用于WinForms应用程序的UI线程?

如果是,为什么?它来自WindowsBase.dll,它似乎是WPF组件。

如果没有,我如何调用工作单元回到UI线程?我找到了Control.BeginInvoke(),但是创建一个控件只是为了引用原始线程似乎很笨拙。

7 个答案:

答案 0 :(得分:18)

即使在WinForms应用程序中也可以使用Dispatcher

如果你确定要在UI线程上(例如在一个button.Click处理程序中),Dispatcher.CurrentDispatcher会为你提供一个UI线程调度程序,你可以在以后用它像往常一样从后台线程调度到UI线程

答案 1 :(得分:12)

Dispatcher是一个WPF组件,而不是WinForms组件。

如果要在UI线程上调度工作项,则必须使用已经找到的Control.BeginInvoke,或者跨线程对ResetEvents / WaitObjects做出反应。

通常在UI线程上调用工作项是件坏事,除非它是UI工作(即更新控件的内容或其他东西),在这种情况下Control.BeginInvoke()就足够了。

答案 2 :(得分:9)

我提供了一个在Windows窗体中使用System.Windows.Threading.Dispatcher的示例in my answer to question“在WinForms上使用TPL进行并行编程”,因为the previous answer to your question

  

如果您确定要在UI线程中(例如,在button.Click处理程序中),   Dispatcher.CurrentDispatcher为您提供UI线程调度程序   您可以像往常一样使用从后台线程调度到UI线程。

要么误导或混淆,要么缺乏具体的使用背景:

  • button.Click处理程序不保证在UI线程上;
  • 如果您不在UI线程上,仍然可以使用WinForms表单的UI线程的调度程序

可以获得WinForm UI线程的调度程序:

Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;

在按钮单击事件处理程序或其他任何地方(在表单构造函数中)

然后使用它在其他线程的UI上执行,请参阅my answer中以下示例的更多详细信息:

private void button1_Click(object sender, EventArgs e)
{
  Dispatcher dispUI = Dispatcher.CurrentDispatcher;
  for (int i = 2; i < 20; i++)
  {
    int j = i;
    var t = Task.Factory.StartNew
           (() =>
      {
        var result = SumRootN(j);
        dispUI.BeginInvoke
            (new Action
                 (() => richTextBox1.Text += "root " + j.ToString()
                       + " " + result.ToString() + Environment.NewLine
                 )
             , null
            );
      }
           );
}

答案 3 :(得分:1)

使用后台工作线程,因为它可以识别UI消息泵,This MSDN Article尽管WPF确实声明BWT即使对于Windows窗体也能识别UI。

答案 4 :(得分:1)

我在使用Oracle依赖类时遇到了类似的问题,该依赖类在Winforms中的自己的线程上运行,

当从Oracle Dependency触发OnChange事件时,我想通过简单设置来显示DataGridView中的更改 DataSource到eventargs.Details(基本上是一个DataTable), 它抛出: System.InvalidOperationException未被用户代码处理   Message =跨线程操作无效:控制'dataGridView1'从其创建的线程以外的线程访问。

StackOverflow用户Brian Peiris(bpeiris@gmail.com),我的同事向我展示了这种方式:

void dep_OnChange(object sender, OracleNotificationEventArgs arg)
         {
         Console.WriteLine("Notification received");

         int infoSum = int.Parse(arg.Details.Compute("Sum(Info)", "Info is not null").ToString());
         InfoSum x = (InfoSum)infoSum;
         foreach (DataRow dr in arg.Details.Rows)
            {
            Console.WriteLine(string.Format("Operation(InfoSum)= {0}", Enum.GetName(typeof(InfoSum), x)));
            Console.WriteLine(string.Format("ontable={0}  Rowid={1},info={2}", dr.Field<string>("ResourceName"), dr.Field<string>("rowid"), dr.Field<Int32>("info")));
            }
         // Following  will throw cross-thread 
         // dataGridView1.DataSource = arg.Details;
         // instead of line above use the following
         dataGridView1.BeginInvoke((Action)(()=>dataGridView1.DataSource = arg.Details));
         IsNotified = true;
         }

      }

答案 5 :(得分:0)

查看backgrounder,看看它是否符合您的需求。

答案 6 :(得分:0)

有时候,在WinForms中设置一个Timer组件非常有用并且很容易,只需设置它的间隔然后启用它,然后确保你在Tick事件处理程序中做的第一件事是禁用它自己。

我认为Timer在自己的线程中运行代码,因此您可能仍需要执行BeginInvoke(调用WinForm对象[this])来运行Action。

private WebBrowserDocumentCompletedEventHandler handler; //need to make it a class field for the handler below (anonymous delegates seem to capture state at point of definition, so they can't capture their own reference)
private string imageFilename;
private bool exit;

public void CaptureScreenshot(Uri address = null, string imageFilename = null, int msecDelay = 0, bool exit = false)
{
  handler = (s, e) =>
   {
     webBrowser.DocumentCompleted -= handler; //must do first

     this.imageFilename = imageFilename;
     this.exit = exit;

     timerScreenshot.Interval = (msecDelay > 0)? msecDelay : 1;
     timerScreenshot.Enabled = true;
   };

  webBrowser.DocumentCompleted += handler;
  Go(address); //if address == null, will use URL from UI
}

private void timerScreenshot_Tick(object sender, EventArgs e)
{
  timerScreenshot.Enabled = false; //must do first

  BeginInvoke((Action)(() => //Invoke at UI thread
  { //run in UI thread

    BringToFront();
    Bitmap bitmap = webBrowser.GetScreenshot();

    if (imageFilename == null)
      imageFilename = bitmap.ShowSaveFileDialog();

    if (imageFilename != null)
    {
      Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(imageFilename))); //create any parent directories needed
      bitmap.Save(imageFilename);
    }

    bitmap.Dispose(); //release bitmap resources

    if (exit)
      Close(); //this should close the app, since this is the main form

  }), null);
}

您可以在WebCapture工具(http://gallery.clipflair.net/WebCapture,源代码:http://ClipFlair.codeplex.com,请参阅工具/ WebCapture文件夹)中查看上述内容,该工具可以从网站上截取屏幕截图。顺便说一句,如果你想从命令行调用可执行文件,请确保你去项目的属性,并在安全选项卡关闭ClickOnce安全性(否则它无法访问命令行)