在 WPF 中使用 BackgroundWorker 更新 UI
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16738719/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Updating UI with BackgroundWorker in WPF
提问by pstricker
I am currently writing a simple WPF 3.5 application that utilizes the SharePoint COM to make calls to SharePoint sites and generate Group and User information. Since this process takes awhile I want to show a ProgressBar while the groups are being generated. The desired process is as follows:
我目前正在编写一个简单的 WPF 3.5 应用程序,它利用 SharePoint COM 调用 SharePoint 站点并生成组和用户信息。由于此过程需要一段时间,我想在生成组时显示 ProgressBar。所需的过程如下:
- User enters url and clicks button to fetch site data.
- ProgressBar begins animation
- Groups are generated and names are added to a ListView
- Upon completion ProgressBar animation ends
- 用户输入 url 并单击按钮以获取站点数据。
- ProgressBar 开始动画
- 生成组并将名称添加到 ListView
- 完成后 ProgressBar 动画结束
The problem I am running into is that the UI is never updated. Neither the ProgressBar or the ListView makes any changes. If anyone has any ideas to help with the code below it would be greatly appreciated.
我遇到的问题是 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;
}
));
}
采纳答案by Kooki
At first you need to support ProgressChangedevents.
Update your BackgroundWorkerinitialization to:
首先你需要支持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);
After that you have to add a OnProgressChangedhandler:
之后,您必须添加一个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);
}
Therefore you have to change your DoWork:
因此你必须改变你的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);
}
}
That's because you're not allowed to change GUI on DoWork.
那是因为您不允许在DoWork.
After that, each ListViewItemis added separately to your ListView. I would also recommend, that your URL is passed as an argument to RunWorkerAsync.
之后,每个ListViewItem都单独添加到您的ListView. 我还建议,您的 URL 作为参数传递给RunWorkerAsync.
Edit: Add percentage to OnProgressChanged.
编辑:将百分比添加到OnProgressChanged.
回答by hbarck
In your DoWork method, you are manipulating WPF controls in code on a background thread, which you are not supposed to do. Actually, you should receive errors like "Cannot access control from other thread". Probably those exceptions are caught by your catch-all error handler, and maybe even the MessageBox doesn't work from the background thread.
在您的 DoWork 方法中,您正在后台线程上操作代码中的 WPF 控件,而您不应该这样做。实际上,您应该会收到诸如“无法从其他线程访问控制”之类的错误消息。可能这些异常被您的全能错误处理程序捕获,甚至 MessageBox 也可能无法在后台线程中工作。
As a quick fix, you would have to make siteURL and collGroups class fields, move everything before the using block to your GetGroupsAndUsersButton_Click method, and everything starting with the first foreach loop to the RunworkerCompleted event, so that all code which accesses controls runs on the UI thread.
作为快速修复,您必须创建 siteURL 和 collGroups 类字段,将 using 块之前的所有内容移动到您的 GetGroupsAndUsersButton_Click 方法,以及从第一个 foreach 循环开始的所有内容到 RunworkerCompleted 事件,以便访问控件的所有代码都在界面线程。
Another thing you should change is that you should not create ListViewItems in code, but use a DataTemplate instead... this is not connected to your problem, though.
你应该改变的另一件事是你不应该在代码中创建 ListViewItems,而是使用 DataTemplate ......但这与你的问题无关。
回答by paul
You'll need:
你需要:
mWorker.WorkerReportsProgress = true;
mWorker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
Then in your DoWorkyou'll need to call:
然后在你DoWork需要调用:
var worker = (BackgroundWorker)sender;
worker.ReportProgress(progressAmount);
Good worked example here: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
很好的例子:http: //msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

