C# WPF DataGrid正在添加额外的;“鬼魂”;一行

C# WPF DataGrid正在添加额外的;“鬼魂”;一行,c#,datagrid,observablecollection,C#,Datagrid,Observablecollection,嘿嘿, 在我的应用程序中,我使用DataGrid来显示一些数据。为了使所有工作都能使用线程,我将其用作DataGrid的DataContext。当我的应用程序启动时,它会在一些文件夹中查找文件并更新AsyncObservableCollection。查找文件是在单独的线程上完成的: Task.Factory.StartNew(() => _cardType.InitAllOrdersCollection()) .ContinueWith((t) => ThrowEvent()

嘿嘿,

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

Task.Factory.StartNew(() => _cardType.InitAllOrdersCollection())
    .ContinueWith((t) => ThrowEvent(), TaskContinuationOptions.None);
其中,所有加载逻辑都位于
InitAllOrdersCollection()
方法中

现在情况就糟了,当我出于某种原因启动应用程序时,我在DataGrid中得到了两行相同的数据,即使集合中有一个项目,文件夹中只有一个文件。如果在加载文件之前添加延迟(
Thread.Sleep()
50ms最小值),那么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);
}
公共AsyncObservableCollection OrdersCollection=new AsyncObservableCollection(); public void InitAllOrdersCollection() { //Sleep(50);IsCorrect(文件,约定)).AsParallel().ToList(); } //将新订单添加到OrdersCollection(如果尚未存在)! 私有void FilesToOrders(列出目录文件、文件名约定) { foreach(dirFiles.AsParallel()中的var dirFile) { var order=新订单(dirFile,this,约定); 如果(!this.OrdersCollection.containersorder(订单)) { this.OrdersCollection.Add(订单); } } } 公共静态bool ContainsOrder(此ObservableCollection集合,订单) { 返回collection.Cast().Any(c=>c.Filepath==order.Filepath); }
FilesToOrders()
方法是将新订单添加到
AsyncObservableCollection
的方法。
希望这能有所帮助。

也许我遗漏了一些明显的东西,但是您发布的链接中的
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);

}

CanUserAddRows=“False”
添加到XAML文件中

<DataGrid CanUserAddRows="False"../>


能否请您添加一些有关InitAllOrdersCollection方法的详细信息?我已经创建了一个简单的测试程序,我没有得到任何副本。也许在您的代码中,我没有做一些事情来复制这种行为。我怀疑问题是否与数据网格控制有关。检查
OrdersCollection
中的内容-您的逻辑可能会在集合中放置一些“空白模型”。另外,为什么要迭代
dirFiles.aspallel()
,而不是只迭代
dirFiles
?在这个循环中是否有任何耗时的操作?OrderCollection只包含适量的项,我已经检查了好几次,但它们仍然在DataGrid中显示为双倍。这里有更多的信息。好的,也许在这些地方调用AsParallel()没有任何作用,但这不是问题所在。我只是猜测,因为我无法在这里轻松地重建它,但看起来datagrid的初始化最初需要一个空列表。额外的50毫秒允许DataGrid完成初始化——这是类似的。您在哪里触发Property Changed事件来告诉DataGrid集合已更改?请查看该博客文章底部的更新解决方案。它使用
SynchronizationContext
将PropertyChanged事件发布到集合线程。@hs2d-是的,我已经看过了。我看不出事件触发有什么问题,但我确实看到访问集合中的项可能会有问题。嗯,这看起来很有趣,当我尝试这个方法时,它确实起了作用。需要进行更多的测试,但我认为我们有一个解决方案。@hs2d我对AsyncObservableCollection也有同样的问题。你完全放弃了吗?你能详细说明一下你对这个问题的最终解决方案吗?像这样
<DataGrid CanUserAddRows="False"../>