我有以下代码向youtube发送查询并将总结果发送到文本框。如果我只是提醒结果,可以,但我无法将结果分配给文本框。请向我解释原因?
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);
}
错误
跨线程操作无效: 从a访问控制'txtSearch' 线程以外的线程 创建于。
答案 0 :(得分:4)
您正在调用BeginInvoke
,因此在线程池线程上调用了您的委托。您无法从该线程池线程访问UI;您需要在控件上调用Invoke
或BeginInvoke
,然后在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; });
答案 1 :(得分:1)
您可以考虑使用BackgroundWorker而不是Delegate.BeginInvoke
。 BackgroundWorker在完成后会引发RunWorkerCompleted
事件,在UI线程中运行,因此您可以在那里更新用户界面。
答案 2 :(得分:0)
由于对Forms控件的访问本质上不是线程安全的,因此调试器会通过从其他线程访问规则来警告您违反规则。相反,您可以直接Invoke
控件来获得所需的结果。有一个很棒的,全面的Microsoft教程,介绍如何执行此操作 here 。
答案 3 :(得分:0)
错误消息告诉您确切的问题所在。您无法安全地操作与创建控件的线程不同的线程上的UI控件;调试器旨在捕获它(有关详细信息,请参阅MSDN)。
因此,您需要在控件上调用BeginInvoke
以便在UI线程上执行,或者您需要在调用的线程和UI线程之间建立某种通信机制。显然前者可以通过TextBox.BeginInvoke
完成:
txtSearch.BeginInvoke(sd, new object[] { keyword, orderBy, SearchCompleted });