9

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

  1. 用户输入 url 并单击按钮以获取站点数据。
  2. ProgressBar 开始动画
  3. 生成组并将名称添加到 ListView
  4. 完成后 ProgressBar 动画结束

我遇到的问题是 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;
        }
        ));
}
4

3 回答 3

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.

之后,每个ListViewItem都单独添加到您的ListView. 我还建议您将 URL 作为参数传递给RunWorkerAsync.

编辑:将百分比添加到OnProgressChanged.

于 2013-05-24T15:56:05.403 回答
2

在您的 DoWork 方法中,您正在后台线程上的代码中操作 WPF 控件,您不应该这样做。实际上,您应该收到诸如“无法从其他线程访问控制”之类的错误。这些异常可能会被您的包罗万象的错误处理程序捕获,甚至可能 MessageBox 在后台线程中也不起作用。

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

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

于 2013-05-24T16:12:24.900 回答
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

于 2013-05-24T15:44:28.320 回答