在WPF中使用BackgroundWorker更新UI

时间:2013-05-24 15:38:19

标签: c# wpf sharepoint backgroundworker

我目前正在编写一个简单的WPF 3.5应用程序,它利用SharePoint COM调用SharePoint站点并生成组和用户信息。由于此过程需要一段时间,因此我希望在生成组时显示ProgressBar。所需的过程如下:

  1. 用户输入网址并点击按钮获取网站数据。
  2. ProgressBar开始动画
  3. 生成组并将名称添加到ListView
  4. 完成后,ProgressBar动画结束
  5. 我遇到的问题是UI永远不会更新。 ProgressBar或ListView都不做任何更改。如果有人有任何想法来帮助解决下面的代码,我们将不胜感激。

    private void GetGroupsAndUsersButton_Click(object sender, RoutedEventArgs e)
    {
        siteUrl = "";
    
        if (SiteURLTextBox.Text.Length > 0)
        {
            FetchDataProgressBar.IsIndeterminate = true;
    
            mWorker = new BackgroundWorker();
            mWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
            mWorker.WorkerSupportsCancellation = true;
            mWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            mWorker.RunWorkerAsync();
        }
        else
        {
            System.Windows.MessageBox.Show("Please enter a URL for the SharePoint site you wish to retrieve data");
        }
    }
    
    private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
    {
        siteUrl = SiteURLTextBox.Text;
        GroupListView.ItemsSource = null;
    
        try
        {
            using (SPSite site = new SPSite(siteUrl))
            {
                SPWeb web = site.OpenWeb();
                SPGroupCollection collGroups = web.SiteGroups;
                if (GroupNames == null)
                    GroupNames = new List<string>();
    
                foreach (SPGroup oGroup in collGroups)
                {
                    GroupListView.Items.Add(new ListViewItem() { Content = oGroup.Name });
                }
    
                foreach (ListViewItem item in GroupListView.Items)
                {
                    item.MouseLeftButtonUp += item_MouseLeftButtonUp;
                }
            }
        }
        catch (Exception ex)
        {
            System.Windows.MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl);
        }
    }
    
    private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        FetchDataProgressBar.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
        new Action(
            delegate()
            {
                FetchDataProgressBar.IsIndeterminate = false;
            }
            ));
    }
    

3 个答案:

答案 0 :(得分:7)

首先,您需要支持ProgressChanged个事件。 将您的BackgroundWorker初始化更新为:

GroupListView.ItemSource = null;
mWorker = new BackgroundWorker();
mWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
mWorker.WorkerSupportsCancellation = true;
mWorker.WorkerReportsProgress = true;
mWorker.ProgressChanged += OnProgressChanged;
mWorker.RunWorkerCompleted +=
        new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync(SiteURLTextBox.Text);

之后你必须添加一个OnProgressChanged处理程序:

private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
    FetchDataProgressBar.Value = e.ProgressPercentage;
    ListViewItem toAdd = (ListViewItem)e.UserState;
    toAdd.MouseLeftButtonUp += item_MouseLeftButtonUp;
    GroupListView.Items.Add(toAdd);
}

因此,您必须更改DoWork

private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    BackgroundWorker worker = (BackgroundWorker)sender;            
    try
    {
        using (SPSite site = new SPSite((String)e.Argument))
        {
            SPWeb web = site.OpenWeb();
            SPGroupCollection collGroups = web.SiteGroups;
            if(GroupNames == null)
                GroupNames = new List<string>();
            int added = 0;
            foreach(SPGroup oGroup in collGroups)
            {
                added++;
                ListViewItem tmp = new ListViewItem() {
                    Content = oGroup.Name
                };
                worker.ReportProgress((added * 100)/collGroups.Count,tmp);
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl);
    }
}

那是因为你不允许在DoWork更改GUI。

之后,每个ListViewItem会分别添加到您的ListView。我还建议您将您的网址作为参数传递给RunWorkerAsync

修改:向OnProgressChanged添加百分比。

答案 1 :(得分:2)

在您的DoWork方法中,您正在操作背景线程中的代码中的WPF控件,您不应该这样做。实际上,您应该收到“无法从其他线程访问控制”等错误。可能这些异常是由catch-all错误处理程序捕获的,甚至MessageBox也无法在后台线程中运行。

作为快速修复,您必须创建siteURL和collGroups类字段,将使用块之前的所有内容移动到GetGroupsAndUsersButton_Click方法,并将所有内容从第一个foreach循环开始循环到RunworkerCompleted事件,以便所有访问控件的代码在UI线程上运行。

您应该更改的另一件事是您不应该在代码中创建ListViewItems,而是使用DataTemplate代替......但这与您的问题无关。

答案 2 :(得分:1)

你需要:

mWorker.WorkerReportsProgress = true;
mWorker.ProgressChanged += 
    new ProgressChangedEventHandler(worker_ProgressChanged);

然后在您的DoWork中,您需要致电:

var worker = (BackgroundWorker)sender;
worker.ReportProgress(progressAmount);

这里的好工作示例:http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx