当我在按钮单击处理程序中的 ObservableCollection 上调用 .Clear() 时,我间歇性地收到此错误。我很困惑,因为我确实有其他 TPL 任务正在运行,但都通过以下方式更新 ObservableCollection:
mainWindow.Dispatcher.Invoke(new Action(delegate()
{
this.myCollection.Add(myItem);
}));
本质上的调度程序是否使我的 ObservableCollection 线程安全,因为所有操作在主 UI 线程上都是原子的?顺便说一句:我也在更新 DispatcherTimer 中集合成员的一些字段,但没有在 timer.tick 处理程序中删除或添加项目到集合中。
詹姆士:
我创建了一个示例应用程序来隔离问题,但该示例一切正常。所以我的应用程序中的其他东西必须在不同的线程上触发collectionchanged。这一切都很好,你可以 .Clear 没有错误:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Name="clear" Height="23" Click="clear_Click"></Button>
<ListBox ItemsSource="{Binding MyCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
public partial class MainWindow : Window
{
private DispatcherTimer myTimer = null;
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Loaded +=new RoutedEventHandler(MainWindow_Loaded);
}
private bool isLoaded = false;
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
if (isLoaded) return;
isLoaded = true;
var task = new Task(() => AddObjectsTask());
task.Start();
myTimer = new DispatcherTimer();
myTimer.Interval = new TimeSpan(0, 0, 1);
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Start();
_MyCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_MyCollection_CollectionChanged);
}
void _MyCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
var query = (from n in _MyCollection select n).ToList();
if (query.Any())
{
foreach (SomeObject o in query)
{
o.Name = (Convert.ToInt64(o.Name) + 1).ToString();
}
}
}
}
void myTimer_Tick(object sender, EventArgs e)
{
this._MyCollection.Add(new SomeObject() { Name = 99.ToString() });
}
void AddObjectsTask()
{
int i = 0;
while(true)
{
Thread.Sleep(10);
this.Dispatcher.Invoke(new Action(delegate()
{
this._MyCollection.Add(new SomeObject() { Name = i.ToString() });
}));
i++;
}
}
private void clear_Click(object sender, RoutedEventArgs e)
{
_MyCollection.Clear();
}
private ObservableCollection<SomeObject> _MyCollection = new ObservableCollection<SomeObject>();
public ObservableCollection<SomeObject> MyCollection
{
get
{
return _MyCollection;
}
}
}
public class SomeObject : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
private string _Name = string.Empty;
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
NotifyPropertyChanged("Name");
}
}
}