我继承了一些通过WCF服务查询数据库的代码,然后在完成后使用回调。我正在尝试向该回调添加一些代码,以便在处理数据时更新UI。我发现在回调期间我无法更新UI:
client.GetDataAsync();
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(GetDataCompleted);
void GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
// Loop through the data
// ...
textBlock1.Text= "test1";
Dispatcher.BeginInvoke(() => textBlock1.Text= "test2" );
var thread = new Thread(() =>
{
// textBlock1.Text= "test3"; (this throws a cross-thread access exception)
Dispatcher.BeginInvoke(() =>
{
textBlock1.Text= "test4";
});
}
thread.Start();
// ...
Debug.WriteLine("done");
}
这些事情都没有更新UI,直到(显然)整个回调完成。这篇文章:
What thread calls the completed event handler on silverlight WCF calls?
建议回调在主UI线程上运行,以便不需要BeginInvoke调用。即使我在上面的代码中添加了各种延迟,它仍然无效。这可能吗?有更好的方法吗?
(这是对此的后续问题:Multiple asynchronous UI updates in Silverlight)
答案 0 :(得分:1)
degorolls建议TPL正确,你的代码看起来如下(除了没有注释)(另外,必须在TPL中处理异常,所以这可能使它不值得,但我不认为它应该) 。 第一种方法将保持不变,并且基于事件的异步编程线程安全性得到处理(即:您总是返回到您调出的同一线程) 我还注意到文本输出都是在做=而不是+ =,但这可能更像是输入溢出的问题 因此,test1和test2将同时打印出来,但是从TPL代码吐出的所有东西都应该打印出来。 UI代码不应该做任何需要太多时间的事情,但是......只更新UI。那么,是否认为这是重构的一个重点? 如果这有帮助,或者我错过了你想要的东西,请告诉我。
client.GetDataAsync();
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(GetDataCompleted);
void GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
// Loop through the data
// ...
textBlock1.Text= "test1";
//////Dispatcher should not be needed here as this IS on the main UI thread
Dispatcher.BeginInvoke(() => textBlock1.Text= "test2" );
//////Everything that happens here should NOT be on the main UI thread, thus the cross-thread access exception
//////You can do Dispatcher.CheckAccess to determine if you need to invoke or not
//////Notice the newCopyOfDataToBeWritten. This is a closure,
//////so using the same referenced object will result in errant data as it loops
//////Also, doing it this way does not guarantee any order that this will be written out
//////This will utilize the parallel fully, but there are ways to force the order
var task = Task.Factory.StartNew(()=>
{
Dispatcher.BeginInvoke(()=>textBlock1.Text += newCopyOfDataToBeWritten)
}
);
// ...
///////I assume this is the end of the loop?
Debug.WriteLine("done");
}
.... 以下基于您发布的内容的代码似乎对我有用
var outsideThread = new Thread(()=>
{
for(int i = 0; i < 20; i++)
{
//This code will show all at once since it is on the main thread,
//which is still running
//If you want this to display one at a time also, then you need
//to use threads and callbacks like below, also
Dispatcher.BeginInvoke(()=>{textBlock1.Text += "outer" + i;});
int newI = i;
var thread = new Thread(() =>
{
System.Threading.Thread.Sleep(1000 * newI);
Dispatcher.BeginInvoke(() =>
{
//This will display as it comes in
textBlock1.Text += "inner" + newI;
});
});
thread.Start();
}
});
outsideThread.Start();