2014-09-16 3 views
0

У меня есть DataGrid, связанный с DataGridCollectionView, который использует Observable Collection. Коллекция содержит около 650 предметов с 20 колонками. Каждые 60 секунд получен новый набор данных. Эти данные сравниваются с существующей коллекцией, а затем добавляются, удаляются и обновляются по мере необходимости. Для обновления я делаю следующее:При обновлении элементов коллекции для DataGrid Асинхронно, почему данные исчезают?

private async void LoadData() 
    { 
     await RefreshData(TimeSpan.FromSeconds(100), cts.Token); 
    } 

private async Task RefreshData(TimeSpan interval, CancellationToken token) 
    { 
     // Get the writable properties for the ContingencyViewModel 
     var writableProperties = typeof(ContingencyViewModel).GetProperties(BindingFlags.Instance | BindingFlags.Public) 
      .Where(p => p.CanWrite); 

     while (!token.IsCancellationRequested) 
     { 
      var list = viewModel.Items; 
      var cvs = GetItems(); // Service call that gets the updated collection 
      var existingIds = list.Select(s => s.UniqueId).Distinct().ToList(); 
      var sourceIds = cvs.Select(s => s.UniqueId).Distinct().ToList(); 

      var toAdd = sourceIds.Except(existingIds).ToList(); 
      var toRemove = existingIds.Except(sourceIds).ToList(); 
      var toUpdate = existingIds.Intersect(sourceIds).ToList(); 

      var itemsToAdd = cvs.Where(x => toAdd.Contains(x.UniqueId)).ToList(); 
      var itemsToRemove = list.Where(x => toRemove.Contains(x.UniqueId)).ToList(); 
      var itemsToUpdate = list.Where(x => toUpdate.Contains(x.UniqueId)).ToList(); 

       // Add new items 
       foreach (ItemViewModel item in itemsToAdd) 
       { 
        list.Add(item); 
       } 

       // Remove dropped items 
       foreach (ItemViewModel item in itemsToRemove) 
       { 
        list.Remove(item); 
       } 

       // Update existing items 
       foreach (ItemViewModel item in itemsToUpdate) 
       { 
        // Get a reference to the Updated Item 
        var source = cvs.First(x => x.UniqueId == item.UniqueId); 

        // This works but locks the UI for a little bit 
        this.UpdateItem<ItemViewModel>(source, item, writableProperties); 

        // This also works but all the results in my grid disappear when I scroll or resize screen. To get them back I have to actually Expand and Collapse groups. 
        /* 
        Action d = delegate() 
        {       
         this.UpdateItem<ItemViewModel>(source, item, writableProperties); 
        }; 

        Dispatcher.CurrentDispatcher.InvokeAsync(d, DispatcherPriority.Normal, token); 
        */ 

       } 

       if (token.IsCancellationRequested) 
        token.ThrowIfCancellationRequested(); 

      if (interval > TimeSpan.Zero) 
       await Task.Delay(interval, token); 
     } 
    } 

private void UpdateItem<T>(T source, T target, IEnumerable<PropertyInfo> properties) 
    { 
     foreach (var p in properties) 
     { 
      var value = p.GetValue(source); 
      p.SetValue(target, value); 
     } 
    } 

Двигается прямо обновление лаги интерфейса, как я ожидал, однако пытаюсь сделать это из другого потока, кажется, вызывают данные исчезает, когда вы прокрутку или изменить размер окна. Я исчезаю, я имею в виду, что строки есть, но они пусты. Единственный способ вернуть его - это свернуть и развернуть группу. Я даже попытался добавить обновления для источника данных (что кажется мне плохой идеей, потому что оно будет вызвано после каждого обновления поля).

Почему данные исчезают при обновлении Async? Есть ли лучший или более подходящий способ сделать эти обновления для элементов, привязанных к datagrid?

+0

Вы должны назвать свою логику обновления из потока пользовательского интерфейса. Попробуйте 'Dispatcher.BeginInvoke (youraction)', когда вы обновляете событие async. – nakiya

ответ

0

Как вы знаете, WPF следует архитектуре STA. Так как правило большого пальца, все обновления должны выполняться в потоке пользовательского интерфейса. Вы можете использовать Диспетчер для приведенного выше сценария: Вы можете прочитать больше о диспетчерском here и here

В идеале вы можете попробовать что-то вроде:

ThreadStart start = delegate() 
{ 
    // make your calls to the db 

    Dispatcher.Invoke(DispatcherPriority.Normal, 
        new Action<object>(UpdateCollection), 
        new object[] { myData }); 
}; 
new Thread(start).Start(); 

private void UpdateCollection(object data) 
{ 
    //iterate your collection and add the data as needed 
} 
+0

Это действительно работает. Он работает лучше, если я передаю всю коллекцию вещей, которые нужно обновить, а не передавать каждый элемент, который нужно обновлять по одному за раз. Я подумал, что это то, что я делал с Диспетчером. CurrentDispatcher.InvokeAsync. Я собираюсь найти разницу между Invoke и InvokeAsync. Еще одно небольшое изменение заключалось в том, что мне пришлось вызвать Application.Current.Dispatcher.Invoke, чтобы получить правильный диспетчер для потока пользовательского интерфейса. – jrandomuser

Смежные вопросы