此消息我处于中断模式:
Inner Exception
Exception: Information for developers (use Text Visualizer to read this):
This exception was thrown because the generator for control 'System.Windows.Controls.ListBox Items.Count:4' with name '(unnamed)' has received sequence of CollectionChanged events that do not agree with the current state of the Items collection. The following differences were detected:
accumulated count 3 is different from actual count 4. [accumulated count is (Count at last Reset + #Adds - #Removes since last Reset).]
At index 1: Generator's item 'Data.DisplayNews' is different from actual item 'Data.DisplayNews'.
One or more of the following sources may have raised the wrong events:
System.Windows.Controls.ItemContainerGenerator
System.Windows.Controls.ItemCollection
System.Windows.Data.ListCollectionView
* Data.AsyncObsetion`1[[Data.DisplayNews,Data,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null]]
(The starred sources are considered more likely to be the cause of the problem.)
The most common causes are (a) changing the collection or its Count without raising a corresponding event,and (b) raising an event with an incorrect index or item parameter.
The exception's stack trace describes how the inconsistencies were detected,not how they occurred. To get a more timely exception,set the attached property 'PresentationTraceSources.TraceLevel' on the generator to value 'High' and rerun the scenario. One way to do this is to run a command similar to the following:\n System.Diagnostics.PresentationTraceSources.SetTraceLevel(myItemsControl.ItemContainerGenerator,System.Diagnostics.PresentationTraceLevel.High)
from the Immediate window. This causes the detection logic to run after every CollectionChanged event,so it will slow down the application.
在代码中,我以这种方式在NewsCollection
中添加了服务器发送的新闻:
void AddNews(byte[] array)
{
for (int i = 0; i < array.Length; i += newsSize)
{
var n = PacMan<Data.News>.Unpack(array.Skip(i).Take(newsSize).ToArray());
NewsCollection.Insert(0,new DisplayNews()
{
Time = n.Time,ItemCode = n.ItemCode,ItemName = Items.First(x => x.Id == n.ItemCode).Name,Feed = n.Feed
});
}
}
NewsCollection
是通过以下方式实现的ObservableCollection
:
public class AsyncObsetion<T> : ObservableCollection<T>
{
private SynchronizationContext context = SynchronizationContext.Current;
public AsyncObsetion() { }
public AsyncObsetion(IEnumerable<T> list) : base(list) { }
void RaiseCollectionChanged(object param) => base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
void RaisePropertyChanged(object param) => base.OnPropertyChanged((PropertyChangedEventArgs)param);
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (SynchronizationContext.Current == context) RaiseCollectionChanged(e);
else context.Send(RaiseCollectionChanged,e);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (SynchronizationContext.Current == context) RaisePropertyChanged(e);
else context.Send(RaisePropertyChanged,e);
}
}
和DisplayNews
已为其所有属性实现了INotifyPropertyChanged
。在xaml中,我拥有这些:
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Source={x:Static VM:ClientCode.NewsCollection}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0 0 0 12" x:Name="newsGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Time}" FontWeight="Bold" Margin="0 0 20 0"/>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding ItemName}" FontWeight="Bold"/>
<TextBlock Grid.Row="1" Text="{Binding Feed}" TextWrapping="Wrap"/>
</Grid>
</Grid>
<DataTemplate.Resources>
<Storyboard x:Key="Appear">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="newsGrid"
Storyboard.TargetProperty="Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</DataTemplate.Resources>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard Storyboard="{StaticResource Appear}"/>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
当服务器一次发送多个新闻时,会发生此错误。如果服务器仅发送一个新闻,则不会出现此错误!如何解决这个问题?
编辑
如果我将ListBox
替换为ListView
,则会遇到相同的错误!唯一没有问题的方法是当我在ItemsControl
中使用ListBox
而不是ListView
或ItemsControl
但没有SelectedItem
的功能时。
编辑
解决方案似乎是在App.Current.Dispatcher.Invoke
函数中添加AddNews
,如下所示:
void AddNews(byte[] array)
{
for (int i = 0; i < array.Length; i += newsSize)
{
var n = PacMan<Data.News>.Unpack(array.Skip(i).Take(newsSize).ToArray());
App.Current.Dispatcher.Invoke(() =>
{
NewsCollection.Insert(0,new DisplayNews()
{
Time = n.Time,Feed = n.Feed
});
});
}
}
context
在public class AsyncObsetion<T> : ObservableCollection<T>
中正在做什么?我已经添加了AsyncObsetion<T>
来避免在用户界面中使用的列表以及在其他确实能够完成工作的所有其他情况下始终避免使用App.Current.Dispatcher.Invoke
。
编辑
现在的问题是,为什么在没有AsyncObsetion<T>
和App.Current.Dispatcher.Invoke
的情况下ItemsControl
可以工作,但是在这种特殊情况下却不能与ListBox
或ListView
一起工作? Framework
是否有问题,或者我在代码中缺少某些内容?
我还有一个名为AsyncObsetion<T>
的{{1}}挂接到了另一个ExecutedOrders
上,它也可以正常工作。 ListView
和ExecutedOrder
之间的唯一区别是,如果News
服务器可以一次发送一个或多个,则服务器一次只能发送一个ExecutedOrder
!
编辑
根据this,在两个构造函数中调用News
是另一种解决方案。要使用它,我必须在我的WPF项目的所有3个(客户端,特殊客户端和服务器)中复制BindingOperations.EnableCollectionSynchronization(this,_lock);
的副本!我将AsyncObsetion<T>
放在一个单独的类库中,所有这3个项目都对此进行了引用。