如何使用BackgroundWorker中处理的数据

时间:2014-03-02 18:55:11

标签: c# multithreading winforms sharepoint backgroundworker

我正在写一个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");           
    }

2 个答案:

答案 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控件。