嵌套的ObservableCollections在不同的线程上修改时抛出异常

时间:2018-03-29 01:18:16

标签: c# wpf mvvm observablecollection

我正在使用一个TreeView,它在我的ViewModel中绑定了一个ObservableCollection的ItemsSource。我正在使用一个HierarchicalDataTemplate,它的ItemsSource绑定到另一个ObservableCollection。这两个ObservableCollections都是从不同的线程动态更新的。

<TreeView x:Name="planeView" BorderThickness="0" MaxHeight="500" ItemsSource="{Binding Planes}"  SelectedItemChanged="treeview_OnSelectedItemChanged">
   <TreeView.Resources>
      <HierarchicalDataTemplate DataType="{x:Type models:Plane}" ItemsSource="{Binding Messages}">
         <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding PlaneId}" />
            <TextBlock Text=" [" Foreground="Blue" />
            <TextBlock Text="{Binding Messages.Count}" Foreground="Blue" />
            <TextBlock Text="]" Foreground="Blue" />
         </StackPanel>
      </HierarchicalDataTemplate>
      <DataTemplate DataType="{x:Type models:Message}">
         <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding TimeStamp}" />
         </StackPanel>
      </DataTemplate>
   </TreeView.Resources>
</TreeView>

最终结果是树,其中顶级节点是动态的,这些节点的内容也是动态的。

当我开始用一个ObservableCollecction开发它时,我遇到了异常:

  

此类型的CollectionView不支持从与Dispatcher线程不同的线程更改其Source Collection

我发现有多个来源建议使用BindingOperations.EnableCollectionSynchronization(...)

这解决了我的问题,我继续发展。但是,当我嵌套它们并使它们都是动态的时候。那个例外又回来了。我确保在两个可观察集合上启用了同步。但是,我仍然有异常,通常异常是在UI中以可视方式显示一两个项目之后。 (没有一致)所以它似乎是某种竞争条件,但我不知道如何解决它。

下面是我的ViewModel和几个支持类。

public class MyViewModel 
{
  private object _lock = new object();

  public ObservableCollection<Plane> Planes { get; set; }

  public MyViewModel()
  {
     Planes = new ObservableCollection<Plane>();
     BindingOperations.EnableCollectionSynchronization(Planes, _lock);
     MessageSystem.Subscribe<PlaneInformationMessage>(HandlePlaneMessage);
  }

  // this method is executed on a different thread
  public void HandlePlaneMessage(PlaneInformationMessage planeMsg)
  {
     Message msg = new Message();

     // set the timestamp
     string timeStampString;
     if (planeMsg.TimeOfDay.HasValue)
     {
        timeStampString = planeMsg.TimeOfDay.Value.ToString(@"hh\:mm\:ss");
     }
     else
     {
        timeStampString = "--:--:--";
     }

     msg.TimeStamp = timeStampString;
     msg.Content = planeMsg.OriginalMessageContents;

     var plane = new Plane();
     plane.PlaneId = planeMsg.TailNumber.ToString();

     int index = Planes.IndexOf(plane);

     if (index < 0)
     {
        plane.Messages.Add(msg);
        Planes.Insert(0, plane);
     }
     else
     {
        Debug.WriteLine(msg.TimeStamp);
        Planes[index].Messages.Insert(0, msg); // This line throws the exception!!
     }
  }

支持课程:

public class Plane : IEquatable<Plane>
{
  private object _lock = new object();

  public string PlaneId { get; set; }

  public ObservableCollection<Message> Messages { get; set; }

  public Plane()
  {
     Messages = new ObservableCollection<Message>();
     BindingOperations.EnableCollectionSynchronization(Messages, _lock);
  }

  public bool Equals(Plane other)
  {
     if (PlaneId == other.PlaneId)
        return true;
     else
        return false;
  }

}

public class Message
{
  public string TimeStamp { get; set; }
  public string Content { get; set; }
  public string Metadata { get; set; }
}

1 个答案:

答案 0 :(得分:0)

您可以尝试使用Dispatcher.Invoke方法:

https://msdn.microsoft.com/es-es/library/system.windows.threading.dispatcher.invoke(v=vs.110).aspx

尝试按以下方式进行更新:

Application.Current.Dispatcher.Invoke(() => Planes[index].Messages.Insert(0, msg));
相关问题