异步添加到ObservableCollection(或替代)

时间:2012-10-14 10:43:39

标签: c# .net wpf data-binding observablecollection

这就是我所拥有的 - 将ItemsSource设置为ObservableCollection<T>的ListBox - 其中T是我的表示文件的自定义类,只包含2个DependencyProperties:Filename和ThumbnailPath。 - 列表框还定义了自定义DataTemplate,以便在其下很好地显示图像和文件名。

列表框的目的是显示当前文件夹中的视频文件(在TreeView中选择),缩略图(异步生成;不是此问题的一部分)。

因此,当我更改TreeView中的文件夹时,ObservableCollection将被清除并再次填充,这将自动反映在ListBox项目中。

问题在于:UI变得无法响应,更新需要几秒钟。同样,缩略图在这里没有意义(我尝试禁用它们)。 我认为花费最多时间的是构建我自定义类的50-100个实例及其可视化表示 - 它必须为每个实例初始化一个Image对象。但这只是我的猜测 - 你能否确认或排除这种可能性?

我开始认为 ObservableCollection可能不是这里的方式,因为根据我的阅读和我尝试过的一点,没有办法异步添加项目,至少如果这些items是DependencyObjects。我尝试使用BackgroundWorker创建我的类实例,并将它们添加到ProgressChanged事件处理程序中的集合中,但它会引发异常(某些线程与依赖项对象问题)。

有什么东西我不见了吗?或者通过简单地抛弃ObservableCollection并编写一个好的旧异步for循环来添加项目,我会更好吗?

3 个答案:

答案 0 :(得分:14)

由于您的ObservableCollection绑定到UI,因此它会在UI线程上生成,因此任何进一步的更新(删除/添加/清除)都必须位于同一UI线程上。它不允许来自另一个线程的更新。

但是,您可以做的是创建类的insance(或在后台线程上执行所有耗时的操作),完成后,使用您的UI线程的Dispatcher添加ObservableCollection中的对象,如下所示: / p>

App.Current.Dispatcher.BeginInvoke((Action)delegate()
                          {
                              observableCollection.Add(instanceOfYourClass);
                          });

Dispatcher做的是将操作放在其关联的线程上。因此,该项将始终添加到UI线程上,但可以在后台线程中创建。

以下几条链接可能会让您前进 - Updating from BW和其他here

答案 1 :(得分:7)

使用.net 4.5,您可以使用EnableCollectionSynchronization

 object lockObj = new object();
        BindingOperations.EnableCollectionSynchronization(yourObservableCollection, lockObj);

答案 2 :(得分:0)

是的,这是一个老问题,我知道,但像这样的问题都结束了。我在 IAsyncEnumerable 存在之前就发现这可能,但现在 IAsyncEnumerable 存在就更容易了。

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,在这个例子中