在WPF中使用BackgroundWorker更新UI
本文关键字:BackgroundWorker 更新 UI WPF | 更新日期: 2023-09-27 18:15:27
我目前正在编写一个简单的WPF 3.5应用程序,它利用SharePoint COM来调用SharePoint站点并生成组和用户信息。由于这个过程需要一段时间,所以我想在生成组时显示一个ProgressBar。所需的过程如下:
- 用户输入url,点击按钮获取站点数据。
- 组被生成,名字被添加到ListView
- 完成后,进度条动画结束
我遇到的问题是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;
}
));
}
首先需要支持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
。
在你的DoWork方法中,你在后台线程的代码中操作WPF控件,这是你不应该做的。实际上,您应该收到诸如"无法从其他线程访问控制"之类的错误。这些异常可能被你的错误处理程序捕获,甚至可能MessageBox不能从后台线程工作。
作为一个快速修复,你将不得不使siteURL和collGroups类字段,移动使用块之前的一切到你的GetGroupsAndUsersButton_Click方法,和一切从第一个foreach循环到RunworkerCompleted事件开始,以便所有访问控件的代码运行在UI线程上。
你应该改变的另一件事是,你不应该在代码中创建listviewitem,而是使用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