7

嘿,

在我的应用程序中,我使用 DataGrid 来显示一些数据。为了让所有线程都工作,我将AsyncObservableCollection其用作 DataGrid 的 DataContext。当我的应用程序启动时,它会在某些文件夹中查找文件并进行更新AsyncObservableCollection。查找文件是在一个单独的线程上完成的:

Task.Factory.StartNew(() => _cardType.InitAllOrdersCollection())
    .ContinueWith((t) => ThrowEvent(), TaskContinuationOptions.None);

所有加载逻辑都在InitAllOrdersCollection()方法中。

现在事情变得糟糕了,当我出于某种原因启动应用程序时,即使集合中有一个项目而文件夹中只有一个文件,我也会在 DataGrid 中获得 2 行具有相同数据的行。Thread.Sleep()如果我在加载文件之前添加延迟(最少 50 毫秒),那么 DataGrid 会正确显示所有内容(没有额外的行)。延迟必须添加到加载文件的线程(使用创建的文件Task.Factory.StartNew())。

有没有人遇到过类似的事情,或者还有什么我应该尝试的吗?提前致谢!

编辑:根据要求添加一些代码:

public AsyncObservableCollection<IGridItem> OrdersCollection = new AsyncObservableCollection<IGridItem>();

public void InitAllOrdersCollection()
{
    // Thread.Sleep(50); <-- this sleep here fixes the problem!
    foreach (var convention in FileNameConventions)
    {
        var namePatterns = convention.NameConvention.Split(',');
        foreach (var pattern in namePatterns)
        {
            var validFiles = CardTypeExtensions.GetFiles(this.InputFolder, pattern, convention);
            if (validFiles.Any())
            {
                this.FilesToOrders(validFiles, convention);
            }
        }
    }
}

public static List<string> GetFiles(string inputFolder, string pattern, FileNameConvention convention)
{
    var files = Directory.GetFiles(inputFolder, pattern);
    return files.Where(file => IsCorrect(file, convention)).AsParallel().ToList();
}

// Adds new order to OrdersCollection if its not there already!
private void FilesToOrders(List<string> dirFiles, FileNameConvention convention)
{
    foreach (var dirFile in dirFiles.AsParallel())
    {
        var order = new Order(dirFile, this, convention);

        if (!this.OrdersCollection.ContainsOrder(order))
        {
                this.OrdersCollection.Add(order);
        }
    }
}

public static bool ContainsOrder(this ObservableCollection<IGridItem> collection, Order order)
{
    return collection.Cast<Order>().Any(c=>c.Filepath == order.Filepath);
}

FilesToOrders()方法是将新订单添加到AsyncObservableCollection. 希望这可以帮助。

4

2 回答 2

5

添加CanUserAddRows="False"到您的 XAML 文件

<DataGrid CanUserAddRows="False"../>
于 2015-01-09T23:19:45.140 回答
3

也许我遗漏了一些明显的东西,但是AsyncObservableCollection您发布的链接中的实现对我来说看起来不是线程安全的。

我可以看到它包含在创建者(消费者)线程上触发 CollectionChanged / PropertyChanged 事件的代码,但我没有看到任何同步以使访问集合线程中的项目是线程安全的。

更新

据我所见,您可以同时发生以下情况,而无需任何同步:

  • 工人(生产者)线程正在插入项目

  • UI(消费者)线程正在枚举项目

一种可能性可能是修改AsyncObservableCollection.InsertItem调用SynchronizationContext.Send以在消费者线程上插入项目,尽管这当然会对性能产生影响(生产者在继续之前等待消费者线程完成插入)。

另一种方法是使用ObservableCollection仅在消费者线程上访问的标准,并用于SynchronizationContext.Post发布项目以从生产者线程插入。就像是:

foreach (var dirFile in dirFiles.AsParallel())
{
    var order = new Order(dirFile, this, convention);

    _synchronizationContext.Post(AddItem, order);

}

...

void AddItem(object item)
{
    // this is executed on the consumer thread
    // All access to OrderCollection on this thread so no need for synchnonization
    Order order = (Order) item;
    if (!OrdersCollection.ContainsOrder(order))
    {
        OrdersCollection.Add(order);
    }
}
于 2012-12-03T17:54:13.347 回答