在WPF中使用BackgroundWorker更新UI
我目前正在编写一个简单的WPF 3.5应用程序,该应用程序利用SharePoint COM调用SharePoint站点并生成组和用户信息。 由于此过程需要一段时间,因此我希望在生成组时显示ProgressBar。 期望的过程如下:
- 用户输入url并单击按钮以获取站点数据。
- ProgressBar开始动画
- 生成组并将名称添加到ListView
- 完成后,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(); 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; } )); }
首先,您需要支持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(); 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
添加百分比。
在您的DoWork方法中,您正在操作背景线程中的代码中的WPF控件,您不应该这样做。 实际上,您应该收到“无法从其他线程访问控制”等错误。 可能这些exception是由catch-allerror handling程序捕获的,甚至MessageBox也无法在后台线程中运行。
作为快速修复,您必须创建siteURL和collGroups类字段,将使用块之前的所有内容移动到GetGroupsAndUsersButton_Click方法,并将所有内容从第一个foreach循环开始循环到RunworkerCompleted事件,以便访问控件的所有代码都在UI线程。
你应该改变的另一件事是你不应该在代码中创建ListViewItems,而是使用DataTemplate代替……但这与你的问题无关。
你需要:
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
- 如何测试使用BackgroundWorker加载的ViewModel?
- File.Exists()返回false,但不在调试中
- BackgroundWorker – 在RunWorkerCompleted中CancellationPending更改为false。 为什么?
- 代表与BackgroundWorker的差异?
- BackgroundWorker&Timer,只读取日志文件的新行?
- 为什么BackgroundWorker不需要在ProgressChanged事件处理程序中调用?
- 如何从BackgroundWorker线程中更新标签?
- 未处理的exception未在C#中捕获
- 将方法传递给backgroundworker dowork