我正在使用一个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; }
}
答案 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));