System.Windows.Threading.Dispatcher
是否适用于WinForms
应用程序的UI线程?
如果是,为什么?它来自WindowsBase.dll,它似乎是WPF
组件。
如果没有,我如何调用工作单元回到UI线程?我找到了Control.BeginInvoke()
,但是创建一个控件只是为了引用原始线程似乎很笨拙。
答案 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线程上; 可以获得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安全性(否则它无法访问命令行)