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