我正在写一个Winform应用程序。它将使用客户端对象模型从Sharepoint 2010查询数据,并根据某些选择执行一些图表。
我的问题是:我希望程序使用后台工作程序从Sharepoint加载数据。一旦后台工作程序完成,我希望它用一些结果填充一些ListBoxes。
我不能这样做,因为
跨线程操作无效:控制'EngineerAccountBox'从其创建的线程以外的线程访问。
在我想要 ListBox.Items.Add。
的地方失败了我以前从未写过backgroundoworker(或winform app),请帮助!
代码:
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string siteUrl = "http://myurl.com";
ClientContext clientContext = new ClientContext(siteUrl);
SP.List oList = clientContext.Web.Lists.GetByTitle("MCS Assignment");
var Yesterday = DateTime.Today.AddDays(-10).ToString("yyyy-MM-ddTHH:mm:ssZ");
var RightNow = DateTime.Today.ToString("yyyy-MM-ddTHH:mm:ssZ");
CamlQuery camlQuery = new CamlQuery();
string query = "<View><Query><Where>" +
"<And>" +
"<Geq><FieldRef Name='Created'/><Value Type='DateTime'>{0}</Value></Geq>" +
"<Leq><FieldRef Name='Created'/><Value Type='DateTime'>{1}</Value></Leq>" +
"</And>" +
"</Where></Query><RowLimit></RowLimit></View>";
camlQuery.ViewXml = string.Format(query, Yesterday, RightNow);
ListItemCollection collListItem = oList.GetItems(camlQuery);
clientContext.Load(collListItem);
clientContext.ExecuteQuery();
foreach (ListItem li in collListItem)
{
FieldUserValue usv = li["EngineerAccount"] as FieldUserValue;
**EngineerAccountBox.Items.Add(usv.LookupValue);**
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("DONE");
}
答案 0 :(得分:0)
你需要一个跨线程调用者。
它应该是这样的:(请注意,它只是一个示例,可能它不会在您的代码中工作,我不知道“LookupValue”的类型,所以根据您的要求定制)
void AddToEngineerAccountBox(LookupValue value)
{
if(EngineerAccountBox.InvokeRequired)
{
EngineerAccountBox.Invoke((Action<LookupValue>)AddToEngineerAccountBox, value);
return;
}
EngineerAccountBox.Items.Add(value);
}
<强>更新强>
@HenkHolterman意识到了一个好点。
使用此代替
void AddToEngineerAccountBox(LookupValue[] value)
{
if (EngineerAccountBox.InvokeRequired)
{
EngineerAccountBox.Invoke((Action<LookupValue[]>)AddToEngineerAccountBox, value);
return;
}
EngineerAccountBox.Items.AddRange(value);
}
并调用该函数
AddToEngineerAccountBox(
collListItem
.Select(_ => _["EngineerAccount"])
.OfType<FieldUserValue>()
.Select(_ => _.LookupValue).
ToArray());
答案 1 :(得分:0)
我建议您在MSDN阅读BackgroundWoker详细信息。
简而言之, BackgroundWorker用于在非UI线程上执行“耗时”工作(这是从Threadpool获取的后台线程)。
为什么呢?因为如果你在UI线程上完成所有工作,那么它将阻止UI线程队列,这实际上会导致UI冻结/延迟用户操作。
BgWorker Thread有什么大不了的,为什么不直接使用ThreadPool.QueueUserWorkItem? 因为您无法直接从非UI线程更新UI控件(这就是您提到的异常的原因)。在这种情况下,您需要使用Control.Invoke / Control.BeginInvoke将控制更新操作显式封送到UI线程。
BackgroundWorker线程不仅仅是从线程池中生成一个线程。它提供了在不同线程上调用的处理程序。 它在UI线程上调用以下处理程序:
ProgressChanged
RunWorkerCompleted
非UI线程上的和“DoWork
”(您正在使用的那个)。
这意味着,您不应该更新“DoWork”处理程序中的UI控件。如果你想这样做,你应该使用“Control.BeginInvoke”,以便编组这样的UI线程更新。
有了这么多细节,我建议“正确”使用BgWorker对象,不要对“DoWork”进行任何UI更新。您可以从DoWork调用ReportProgress,它将调用“RunWorkerCompleted”处理程序(在UI线程上),以便您更新UI控件。