这就是我所拥有的 - 一个 ListBox,其 ItemsSource 设置为
ObservableCollection<T>
- 其中 T 是代表文件的自定义类,仅包含 2 个 DependencyProperties:文件名和缩略图路径。
- 列表框还定义了自定义 DataTemplate,以便在其下很好地显示图像和文件名。
列表框的目的是显示当前文件夹中的视频文件(在 TreeView 中选择),并带有缩略图(异步生成;不是此问题的一部分)。
因此,当我更改 TreeView 中的文件夹时,ObservableCollection 会被清除并再次填充,这会自动反映在 ListBox 项目中。
问题在于:UI 变得无响应,并且更新需要几秒钟的时间。同样,缩略图在这里没有意义(我尝试禁用它们)。 我认为花费最多时间的是构建我的自定义类的 50-100 个实例及其视觉表示 - 它必须为每个实例初始化一个 Image 对象。但这只是我的猜测 - 您能否确认或排除这种可能性?
我开始认为ObservableCollection可能不是正确的选择,因为从我读到的内容和我尝试过的一些内容来看,没有办法异步添加项目,至少如果这些项目是DependencyObjects的话。我尝试使用 BackgroundWorker 创建类实例并将它们添加到 ProgressChanged 事件处理程序中的集合中,但它引发异常(一些线程与依赖对象问题)。
我有什么遗漏的吗?或者我只需放弃 ObservableCollection 并编写一个好的旧异步 for 循环来添加项目会更好吗?
由于您的
ObservableCollection
绑定到 UI,因此它是在 UI 线程上生成的,因此任何进一步的更新(删除/添加/清除)都必须在同一 UI 线程上进行。它不允许从另一个线程进行更新。
但是,您可以做的是创建类的实例(或在后台线程上进行所有耗时的操作),完成后,使用 UI 线程的
Dispatcher
将对象添加到 ObservableCollection 中,如下所示 -
App.Current.Dispatcher.BeginInvoke((Action)delegate()
{
observableCollection.Add(instanceOfYourClass);
});
Dispatcher所做的就是将操作放在其关联的线程上。因此,该项目将始终添加在 UI 线程上,但可以在后台线程中创建。
这里有一些链接可能会让您继续前进 -从 BW 更新,另一个是 这里
object lockObj = new object();
BindingOperations.EnableCollectionSynchronization(yourObservableCollection, lockObj);
ViewModel 类中的属性
public ObservableCollection<Card> Cards { get; set; }
ViewModel 构造函数
public CardViewModel(IDbContextFactory<CardContext> ccf)
{
this.ctxFactory = ccf; //DB Context, using DI
this.Cards = new ObservableCollection<Card>();
Task.Run(async () =>
{
await foreach (Card card in GetAllCards())
{
this.Cards.Add(card);
}
});
}
private IAsyncEnumerable<Card> GetAllCards()
{
CardContext cc = this.ctxFactory.CreateDbContext();
return cc.Cards
.Include(cc => cc.Edition)
.Include(cc => cc.Site)
.Include(cc => cc.Condition)
.Include(cc => cc.Purchases)
.Include(cc => cc.Trades)
.AsNoTracking()
.AsAsyncEnumerable();
}
卡片在视图中绑定到数据网格
ItemsSource="{Binding Cards}"
“卡片”每次添加到网格中,我可以在网格加载时与网格交互,并且应用程序不会冻结。数据来自使用 EF Core 5 的数据库,在本例中就是 CardContext
解决方案。
public class AsyncObservableCollection<T> : ObservableCollection<T>
{
private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (SynchronizationContext.Current == _synchronizationContext)
{
RaiseCollectionChanged(e);
}
else
{
_synchronizationContext.Send(RaiseCollectionChanged, e);
}
}
private void RaiseCollectionChanged(object param)
{
base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (SynchronizationContext.Current == _synchronizationContext)
{
RaisePropertyChanged(e);
}
else
{
_synchronizationContext.Send(RaisePropertyChanged, e);
}
}
private void RaisePropertyChanged(object param)
{
base.OnPropertyChanged((PropertyChangedEventArgs)param);
}
}