我正在尝试实现以下要求(C#4.0);
ListView
控件,它被数据绑定到待处理的上传请求集合到目前为止,我还没有能够在不使用两个集合的情况下解决上述问题,如下所示;
public void AddUploadRequest(UploadRequest uploadRequest)
{
this.UploadRequests.Add(uploadRequest);
this.uploadRequestsBlocking.Add(uploadRequest);
}
...其中UploadRequests
是ObservableCollection<UploadRequest>
而uploadRequestsBlocking
是BlockingCollection<UploadRequest>
完整的代码可以在这里找到; http://pastebin.com/620EqaY5(忽略Dispatcher的大量注入 - 到目前为止这只是原型代码)
我通知我的更新UI如下;
this.dispatcher.Invoke(() => this.UploadRequests.Remove(uploadRequest));
有没有更好的方法来实现此功能?或者,更重要的是,这种方法有任何严重的缺点吗?
我考虑的一个可能的扩展是使用Parallel.ForEach
而不是GetConsumingPartitioner
(基于this示例),有多个消费者。关于当前方法的任何事情都会使这个不合适吗?它确实可以正常工作,但我并不是100%有信心我在这个过程中没有提供过一些主要的线程故障。
答案 0 :(得分:2)
您只能将数据绑定到BlockingCollection的主要原因是它没有实现INotifyCollectionChanged,因此在添加/删除请求时UI不会更新。也就是说,BlockingCollection wrappers around on the net实现了INotifyCollectionChanged。或者您可以为BlockingCollection公开CollectionView,而不是从ObservableCollection调度remove,只需调度CollectionView的Refresh。
public ICollectionView UploadRequestsView {get;set;}
public UploadRequester(Dispatcher dispatcher)
{
this.dispatcher = dispatcher;
this.uploadRequestsBlocking = new BlockingCollection<UploadRequest>();
UploadRequestsView = CollectionViewSource.GetDefaultView(uploadRequestsBlocking);
this.consumerTask = Task.Factory.StartNew(this.ConsumeUploadRequests);
}
public void AddUploadRequest(UploadRequest uploadRequest)
{
uploadRequestsBlocking.Add(uploadRequest);
UploadRequestsView.Refresh()
}
private void ConsumeUploadRequests()
{
foreach (var uploadRequest in this.uploadRequestsBlocking.GetConsumingEnumerable())
{
uploadRequest.Status = "Uploading...";
Thread.Sleep(2000);
uploadRequest.Status = "Successfully uploaded";
Thread.Sleep(500);
dispatcher.Invoke(() => UploadRequestsView.Refresh());
}
}
答案 1 :(得分:1)
我会使用TPL
而不需要注入Dispatcher
,因为TaskScheduler.FromCurrentSynchronizationContext()
会给我Dispatcher
线程的当前MainUI
。
public class UploadRequestProcessor
{
public ObservableCollection<UploadRequest> UploadRequests { get; private set; }
private readonly BlockingCollection<UploadRequest> uploadRequestsBlocking;
private Task consumerTask;
public UploadRequester()
{
this.UploadRequests = new ObservableCollection<UploadRequest>();
this.uploadRequestsBlocking = new BlockingCollection<UploadRequest>();
this.consumerTask = Task.Factory.StartNew(this.ConsumeUploadRequests).ContinueWith(nextTask => RemoveUploadRequests(nextTask.Result as BlockingCollection<UploadRequest>), TaskScheduler.FromCurrentSynchronizationContext());
}
public void AddUploadRequest(UploadRequest uploadRequest)
{
this.UploadRequests.Add(uploadRequest);
this.uploadRequestsBlocking.Add(uploadRequest);
}
private void ConsumeUploadRequests()
{
var requestsToRemove = new BlockingCollection<UploadRequest>();
foreach (var uploadRequest in this.uploadRequestsBlocking.GetConsumingEnumerable())
{
uploadRequest.Status = "Uploading...";
Thread.Sleep(2000);
uploadRequest.Status = "Successfully uploaded";
Thread.Sleep(500);
requestsToRemove.Add(uploadRequest);
}
return requestsToRemove;
}
private void RemoveUploadRequests(BlockingCollection<UploadRequest> requestsToRemove)
{
foreach(var request in requestsToRemove)
{
UploadRequests.Remove(request);
}
}
}