我尝试更新WPF TreeView
,其中每个根节点都包含多个子节点,因此我按需加载子节点""使用TreeView_Expanded
事件。
当节点扩展时,将调用ViewModel中的FillFolder
方法。此方法以这种方式调用主RunWorkerAsync
上的BackgroundWorker
:
public void FillFolder(SCADAFolder selectedFolder)
{
_bwWorker.CancelAsync();
_bwWorker.RunWorkerAsync(selectedFolder);
}
我调用了CancelAsync
方法,因为如果我扩展了一个包含数千个子节点的节点,在它结束之前我打开了另一个子节点,那么这个子节点将被填充。第一个节点打开,因为循环仍在添加项目。
这是我的BackroundWorker声明的方式:
private BackgroundWorker _bwWorker;
//Constructor
{
_bwWorker = new BackgroundWorker();
_bwWorker.WorkerReportsProgress = true;
_bwWorker.WorkerSupportsCancellation = true;
_bwWorker.DoWork += _bwWorker_DoWork;
_bwWorker.ProgressChanged += _bwWorker_ProgressChanged;
_bwWorker.RunWorkerCompleted += _bwWorker_RunWorkerCompleted;
[...]
}
这就是BackgroundWorker
实际做的事情:
void _bwWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
[do a query...]
foreach (DataRowView rowView in QueryResults)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
selectedFolder.Items.Clear();
break;
}
else
{
MyItem item = CreateItem();//pseudo code here
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => selectedFolder.Items.Add(item)));
}
}
}
但CancellationPending
条件永远不会成立。
为什么?
修改
使用ThreadPool.QueueUserWorkItem(...)
代替BackgroundWorker
解决并使用Invoke
代替BeginInvoke
答案 0 :(得分:0)
您如何测试取消是否有效?您唯一的测试是继续添加项目吗?您是通过BeginInvoke
添加商品的。由于这是异步的,您的工作人员可能已经完成但仍继续添加项目。
您有竞争条件,因为CancelAsync
不等待工人停止。您应该将selectedFolder
作为参数传递给worker,并在执行追加时使用该参数,而不是使用当时恰好选择的文件夹。
请注意,selectedFolder.Items.Clear();
也需要在BeginInvoke
范围内调用。