2010-01-17 44 views
1

我有以下代码将查询发送到youtube并将总结果发送到文本框。如果我只是提醒结果,没关系,但我不能将结果分配给文本框。请向我解释为什么?NET中的BeginInvoke问题

private void SearchVideo(string keyword) 
{ 
    string orderBy = ""; 
    switch (cboSortBy.SelectedIndex) 
    { 
     case 1: orderBy = "published"; break; 
     case 2: orderBy = "viewCount"; break; 
     case 3: orderBy = "rating"; break; 
     default: orderBy = "relevance"; break; 
    } 
    SearchDelegate sd = Search; 
    sd.BeginInvoke(keyword, orderBy, SearchCompleted, sd); 
} 

private void SearchCompleted(IAsyncResult ar) 
{ 
    if (null == ar) return; 
    SearchDelegate sd = ar.AsyncState as SearchDelegate; 
    Feed<Video> result = sd.EndInvoke(ar); 
    txtSearch.Text = result.TotalResults.ToString(); 
} 

private Feed<Video> Search(string keyword, string orderBy) 
{ 
    YouTubeQuery query = new YouTubeQuery(YouTubeQuery.DefaultVideoUri); 
    query.OrderBy = orderBy; 
    query.Query = keyword; 
    query.SafeSearch = YouTubeQuery.SafeSearchValues.None; 
    return GetRequest().Get<Video>(query); 
} 

和错误

跨线程操作无效: 控制“txtSearch”从比它的线程上创建 以外的 线程访问。

回答

4

您打电话给BeginInvoke,以便在线程池线程上调用您的委托。您无法从该线程池线程访问UI;您需要在控件上调用InvokeBeginInvoke,然后在UI线程上使用结果。例如,使用匿名方法:

txtSearch.BeginInvoke((MethodInvoker) delegate() 
    { txtSearch.Text = result.TotalResults.ToString(); } 
); 

或使用lambda表达式,并配有独立的局部变量只是为了清晰:

MethodInvoker action=() => { txtSearch.Text = result.TotalResults.ToString();}; 
txtSearch.BeginInvoke(action); 

使用Invoke将使调用线程阻塞,直到UI线程已经援引了代表; BeginInvoke是非阻塞的。

编辑:如果问题是result.TotalResults是需要很长的时间了位,做到位仍然在后台线程:

string text = result.TotalResults.ToString(); 
txtSearch.BeginInvoke((MethodInvoker) delegate() { txtSearch.Text = text; }); 
+0

BeginInvoke仍然阻止GUI :( – 2010-01-17 15:15:10

+1

@新鸡:你究竟是什么意思?BeginInvoke在调用委托和控件时都是非阻塞的。以我描述的方式使用BeginInvoke * will *会工作,并且不会阻塞UI。如果您遇到问题,请给出一个简短但完整的程序来演示此问题。 – 2010-01-17 15:50:02

+0

这是我在C#2008EE中的项目http://www.mediafire.com/?n220ddoi2i2 请检查我,谢谢你。 – 2010-01-17 16:04:55

0

因为访问窗体控件​​本质上不是线程安全的,调试器会警告您,您通过从其他线程访问它而违反了规则。相反,你可以直接控制Invoke以获得你想要的结果。有一个伟大的,全面的微软教程如何做到这一点here

+0

使用线程安全的方法花费更长的时间比正常:( – 2010-01-17 15:48:29

0

错误消息告诉你到底是什么问题。您不能安全地在与创建控件的线程不同的线程上操作UI控件;调试器的设计目的是为了解决这个问题(详见MSDN)。

因此,您需要在控件上调用BeginInvoke,以便它在UI线程上执行,或者您需要在调用的线程和UI线程之间设置一些通信机制。显然,前者可以简单地用TextBox.BeginInvoke来完成:

txtSearch.BeginInvoke(sd, new object[] { keyword, orderBy, SearchCompleted }); 
1

相反Delegate.BeginInvoke的你可以考虑使用一个BackgroundWorker。 BackgroundWorker在完成后引发RunWorkerCompleted事件,其中在UI线程中运行,因此您可以在那里更新您的用户界面。

+0

我已经尝试过BackgroundWorker的,但它仍然阻止用户界面: ( – 2010-01-17 14:48:31

+0

这很奇怪;我已经在很多项目中成功地使用了BackgroundWorker,他们不*拦截UI。你确定你使用它正确吗?在你的事件处理程序中:只需启动BackgroundWorker(没有其他)。 DoWork:执行工作,在BW.RunWorkerCompleted中:更新UI – Heinzi 2010-01-17 15:04:30

+2

访问属性TotalResults是否可以实际执行所有工作?在这种情况下,您应该将该属性移动到后台线程,并只传递结果字符串到UI线程 – Heinzi 2010-01-17 15:23:49