System.Windows.Threading.Dispatcher和WinForms?

System.Windows.Threading.Dispatcher和WinForms?,winforms,multithreading,dispatcher,begininvoke,Winforms,Multithreading,Dispatcher,Begininvoke,是否在WinForms应用程序的UI线程上工作 若有,原因为何?它来自WindowsBase.dll,它似乎是一个WPF组件 如果没有,如何将工作单元调用回UI线程?我发现了,但是创建一个只引用原始线程的控件似乎很笨拙。Dispatcher是一个WPF组件,而不是WinForms组件 如果要在UI线程上分派工作项,则必须使用已经找到的Control.BeginInvoke,或者跨线程对ResetEvents/WaitObjects作出反应 通常,在UI线程上调用工作项是一件坏事,除非它是一项UI

是否在
WinForms
应用程序的UI线程上工作

若有,原因为何?它来自WindowsBase.dll,它似乎是一个
WPF
组件


如果没有,如何将工作单元调用回UI线程?我发现了,但是创建一个只引用原始线程的控件似乎很笨拙。

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

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


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

使用后台工作线程,因为它可以识别UI消息,虽然大部分关于WPF的内容都表明,即使对于windows窗体,BWT也支持UI。

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


如果您确定位于UI线程上(例如,在按钮中,单击处理程序),
Dispatcher.CurrentDispatcher
为您提供了UI线程调度程序,您以后可以使用它像往常一样从后台线程调度到UI线程。

看看它是否适合您的需要。

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

当从Oracle依赖项触发OnChange事件时,我希望通过简单的设置在DataGridView中显示更改 eventargs.Details(本质上是一个数据表)的数据源, 它抛出: 用户代码未处理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;
         }

      }
void dep_OnChange(对象发送方,OracleNotificationEventArgs arg)
{
Console.WriteLine(“收到通知”);
int infoSum=int.Parse(arg.Details.Compute(“Sum(Info)”,“Info不为空”).ToString();
InfoSum x=(InfoSum)InfoSum;
foreach(arg.Details.Rows中的数据行dr)
{
WriteLine(string.Format(“Operation(InfoSum)={0}”,Enum.GetName(typeof(InfoSum),x));
Console.WriteLine(string.Format(“ontable={0}Rowid={1},info={2}”)、dr.Field(“ResourceName”)、dr.Field(“Rowid”)、dr.Field(“info”);
}
//下面将抛出交叉线程
//dataGridView1.DataSource=arg.Details;
//使用以下命令代替上面的行
dataGridView1.BeginInvoke((操作)(()=>dataGridView1.DataSource=arg.Details));
IsNotified=true;
}
}

我提供了一个在Windows窗体“在WinForms上使用TPL并行编程”中使用
System.Windows.Threading.Dispatcher
的示例,因为:

如果您确定在UI线程中(例如,在按钮中,单击处理程序), Dispatcher.CurrentDispatcher为您提供了 您可以像往常一样使用从后台线程分派到UI线程

具有误导性或混淆性,或缺乏具体的使用上下文:

  • 按钮。单击
    处理程序不保证在UI线程上
  • 如果您不在UI线程上,仍然可以使用WinForms窗体的UI线程的disparcher
可以获取WinForm UI线程的调度程序:

Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;
在任一按钮中,单击事件处理程序或任何其他位置(在表单构造函数中)

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

private void按钮1\u单击(对象发送者,事件参数e)
{
Dispatcher dispUI=Dispatcher.CurrentDispatcher;
对于(int i=2;i<20;i++)
{
int j=i;
var t=Task.Factory.StartNew
(() =>
{
var结果=SumRootN(j);
dispUI.BeginInvoke
(新行动)
(()=>richTextBox1.Text+=“根”+j.ToString()
+“”+result.ToString()+Environment.NewLine
)
无效的
);
}
);
}

有时,计时器组件在WinForms中很有用且易于设置,只需设置其间隔,然后启用它,然后确保在其Tick事件处理程序中执行的第一件事是禁用它本身

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

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工具(源代码位于:,请参阅Tools/WebCapture文件夹)上看到上述操作,该工具可从网站抓取屏幕截图。顺便说一句,如果要从命令行调用可执行文件,请确保转到项目的属性,并在“安全”选项卡上关闭ClickOnce安全(否则它无法访问命令行)

这是肯定的还是否定的?WinForms interop是如何发挥作用的?a)背景是GPL。因此,它只对同样是GPL的项目有用。B)如果您使用的是.NET 4或更高版本,则此功能内置于API中。请参阅。
使用System.Threading;
无法找到类型或命名空间“Dispatcher”“button.Click处理程序不保证在UI线程上”然后“可以在任意一个button Click事件处理程序中获取WinForm UI线程的调度程序[…]”。这有什么不同?除了在窗体构造函数中获取调度程序外,我们如何确保我们在UI线程中?
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);
}