【问题标题】:Updating UI with BackgroundWorker in WPF在 WPF 中使用 BackgroundWorker 更新 UI
【发布时间】:2017-08-05 16:22:13
【问题描述】:

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

  1. 用户输入网址并点击按钮获取站点数据。
  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;
        }
        ));
}

【问题讨论】:

    标签: c# wpf sharepoint backgroundworker


    【解决方案1】:

    首先你需要支持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。我还建议您将 URL 作为参数传递给 RunWorkerAsync

    编辑:将百分比添加到OnProgressChanged

    【讨论】:

    • 感谢您的回复。可悲的是,仍然没有 GUI 更新。此外,在调试时,进度更改事件永远不会受到影响。它会立即在 worker_RunWorkerCompleted 处中断 (???)
    • 你有没有进入 try/catch 循环?
    • 经过一番修改后,我能够通过将引用 UI 元素(GroupListView 和 SiteUrlTextBox)的代码移动到按钮单击事件来达到 try/catch,但是现在它在启动ListViewItem: "调用线程必须是 STA,因为很多 UI 组件都需要这个。"
    • 将 ListViewItem 创建移动到进度更改事件中,它的工作原理就像一个魅力。感谢您的帮助!
    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

      你需要:

      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

      【讨论】:

      • 感谢您的回复。不幸的是,我并没有尝试用值更新进度条,我只想显示 Indeterminate 动画。
      • 如果是这种情况,您是否只需要调用.IsIndeterminate = true; 就像调用.IsIndeterminate = false; 一样
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-23
      • 1970-01-01
      • 2010-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多