2012-11-29 36 views
6

Hei,WPF DataGrid đang thêm hàng "ma" bổ sung

Trong ứng dụng của tôi, tôi đang sử dụng DataGrid để hiển thị một số dữ liệu. Để có được tất cả mọi thứ làm việc với luồng tôi đang sử dụng AsyncObservableCollection như DataContext của DataGrid. Khi ứng dụng của tôi bắt đầu, nó sẽ tìm kiếm các tệp trong một số thư mục và cập nhật AsyncObservableCollection. Tìm file được thực hiện trên một thread riêng biệt:

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

đâu tất cả các logic tải là trong InitAllOrdersCollection() phương pháp.

Bây giờ đây là nơi mọi thứ trở nên xấu, khi tôi khởi động ứng dụng vì lý do nào đó tôi nhận được 2 hàng có cùng dữ liệu trong DataGrid ngay cả khi có một mục trong bộ sưu tập và chỉ một tệp trong thư mục. Nếu tôi thêm độ trễ (tối thiểu Thread.Sleep() 50ms) trước khi tải tệp thì DataGrid hiển thị mọi thứ chính xác (không có hàng bổ sung). Trì hoãn phải được thêm vào Chủ đề những gì đang tải các tập tin (Một trong những tạo ra với Task.Factory.StartNew()).

Có ai gặp phải điều gì đó tương tự hoặc có điều gì khác tôi nên thử không? Cảm ơn bạn trước!

EDIT: Thêm một số mã như yêu cầu:

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() phương pháp là một trong những bổ sung thêm các đơn đặt hàng mới đến AsyncObservableCollection. Hy vọng điều này sẽ hữu ích.

+0

Ông có thể thêm một số chi tiết về phương pháp InitAllOrdersCollection? Tôi đã tạo một chương trình thử nghiệm đơn giản và tôi không nhận được bất kỳ bản sao nào. Có lẽ có cái gì đó trong bạn mã mà tôi không làm để nhân rộng loại hành vi này. –

+0

Tôi nghi ngờ vấn đề đã làm với điều khiển lưới dữ liệu. Kiểm tra nội dung trong 'OrdersCollection' - logic của bạn có thể đang đặt một số" mẫu trống "trong bộ sưu tập. Ngoài ra, tại sao bạn lặp lại thông qua 'dirFiles.AsParallel()' thay vì chỉ 'dirFiles'? Có hoạt động tốn thời gian nào trong vòng lặp đó không? –

+0

OrderCollection chỉ chứa đúng số lượng mục, tôi đã kiểm tra nhiều lần, nhưng chúng vẫn được hiển thị gấp đôi trong DataGrid. Dưới đây là một số thông tin thêm về [AsParallel()] (http://www.albahari.com/threading/part5.aspx#_PLINQ). Ok, có thể gọi AsParallel() ở những nơi không làm gì cả, nhưng đó không phải là vấn đề ở đây .. – hs2d

Trả lời

2

Có thể tôi thiếu điều gì đó hiển nhiên, nhưng việc triển khai AsyncObservableCollection trong liên kết mà bạn đã đăng không có vẻ an toàn đối với tôi.

Tôi có thể thấy nó bao gồm mã để kích hoạt các sự kiện CollectionChanged/PropertyChanged trên chuỗi người tạo (người tiêu dùng), nhưng tôi không thấy bất kỳ đồng bộ hóa nào để truy cập vào các mục trong bộ sưu tập an toàn chỉ.

CẬP NHẬT

Theo như tôi có thể thấy bạn có thể có xảy ra sau đồng thời, mà không cần bất kỳ đồng bộ hóa:

  • người lao động (sản xuất) thread được chèn mục (s)

  • luồng giao diện người dùng (người tiêu dùng) đang liệt kê các mục

Một khả năng có thể là sửa đổi AsyncObservableCollection.InsertItem để gọi SynchronizationContext.Send để chèn mục vào chuỗi người tiêu dùng, mặc dù điều này tất nhiên sẽ ảnh hưởng đến hiệu suất (nhà sản xuất chờ chuỗi tiêu dùng hoàn thành chèn trước khi tiếp tục).

Phương pháp thay thế sẽ là sử dụng tiêu chuẩn ObservableCollection chỉ được truy cập trên chuỗi tiêu thụ và sử dụng SynchronizationContext.Post để đăng các mục để chèn từ chuỗi nhà sản xuất. Một cái gì đó như:

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); 
    } 
} 
+0

Xem giải pháp được cập nhật ở cuối bài đăng trên blog đó. Sử dụng 'SynchronizationContext' của nó để đăng sự kiện PropertyChanged lên luồng thu thập. – hs2d

+0

@ hs2d - vâng tôi đã xem xét điều đó. Tôi không thấy sự cố khi kích hoạt sự kiện nhưng tôi thấy có vấn đề với việc truy cập các mục trong bộ sưu tập. – Joe

+0

Hmm, có vẻ thú vị và khi tôi đã thử nó thực sự làm việc. Cần phải làm một số bài kiểm tra nhiều hơn nhưng tôi nghĩ rằng chúng tôi có một giải pháp. – hs2d

4

Thêm CanUserAddRows="False" đến file XAML của bạn

<DataGrid CanUserAddRows="False"../> 
+1

Như thế này Lana

Các vấn đề liên quan