Summary of differences between ObservableCollection and List
The simplest difference between their classes is their methods of inheritance. (learning skills will make you get twice the result with half the effort and improve your efficiency.)
study
ObservableCollection is relatively simple. It inherits collection, inotifycollectionchanged and inotifypropertychanged
spa
Collection: Provides a base class for a generic collection. code
INotifyCollectionChanged: notifies listeners of dynamic changes to the collection, such as when to add and remove items or reset the entire collection object. object
INotifyPropertyChanged: notifies the client that a property value has changed. sort
Therefore, the ObservableCollection method does not do much data operation, and focuses on the event that will call the notification when its own ability changes (whether it is an attribute or a collection). (usually used to update the UI, although it can also be used to write other things. This will be written later) inheritance
There are more lists, which inherit IList, ICollection, IEnumerable, IList, ICollection, IEnumerable. Indexes
IList: Represents a set of objects that can be accessed individually by index. Interface
ICollection: defines a method for manipulating a generic collection. event
IEnumerable: exposes an enumerator that supports simple iterations over collections of a specified type. get
IList: represents a non generic collection of objects that can be accessed individually by index.
ICollection: defines the size, enumerator, and synchronization method of all non generic collections.
IEnumerable: exposes an enumerator that supports simple iterations over non generic collections.
Then make a demo to see the effect.
<ListBox x:Name="listbind" Height="61" HorizontalAlignment="Left" Margin="146,12,0,0" VerticalAlignment="Top" Width="120" > <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <ListBox x:Name="observbind" Height="74" HorizontalAlignment="Left" Margin="146,111,0,0" VerticalAlignment="Top" Width="120" > <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <TextBlock Height="23" HorizontalAlignment="Left" Margin="38,58,0,0" Name="textBlock1" Text="List Binding data" VerticalAlignment="Top" /> <TextBlock Height="44" HorizontalAlignment="Left" Margin="12,125,0,0" Name="textBlock2" Text="ObservableCollection Binding data" VerticalAlignment="Top" Width="112" /> <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="77,211,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
xaml page is very simple. Two listbox es are used to bind ObservableCollection and List respectively
public class Person { public string Name { get; set; } }
Entity class.
private List<Person> person1 = new List<Person>(); private ObservableCollection<Person> person2 = new ObservableCollection<Person>(); public DemoTestDiff() { InitializeComponent(); person1.Add(new Person() { Name = "Zhang San" }); person1.Add(new Person() { Name = "Li Si" }); listbind.ItemsSource = person1; person2.Add(new Person() { Name = "Zhang San" }); person2.Add(new Person() { Name = "Li Si" }); observbind.ItemsSource = person2; } private void button1_Click(object sender, RoutedEventArgs e) { person1.Add(new Person() { Name = "Wang Wu" }); person2.Add(new Person() { Name = "Wang Wu" }); }
Run the program and click the button
Then only ObservableCollection is added. Indicates that when the collection of collection objects changes, only ObservableCollection will issue a notification to update the UI. This is just one of their two differences.
in summary:
ObservableCollection represents a dynamic data collection that provides notifications when items are added, removed, or the entire list is refreshed.
List represents a strongly typed list of objects that can be accessed through the index. Provides methods for searching, sorting, and manipulating lists. (Linq is used for most operations, which is very powerful and convenient.)
Problem introduction
When the ObservableCollection list is occupied by the UI thread, if the ObservableCollection is invoked in the asynchronous thread, the following exception will be popped up.
problem analysis
Let's use a ViewModel and add the ItemsSource list of ObservableCollection type in the ViewModel. asynchronous
Bind ItemsSource list with ListBox in the list. Then the interface triggers the modification of ItemsSource.
1 public class ViewModel : INotifyPropertyChanged 2 { 3 private ObservableCollection<string> _itemsSource = new ObservableCollection<string>(); 4 5 public ObservableCollection<string> ItemsSource 6 { 7 get => _itemsSource; 8 set 9 { 10 _itemsSource = value; 11 OnPropertyChanged(); 12 } 13 } 14 15 public event PropertyChangedEventHandler PropertyChanged; 16 17 [NotifyPropertyChangedInvocator] 18 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 19 { 20 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 21 } 22 }
View Code
- Modify ObservableCollection directly under asynchronous thread - report this error
- private void ButtonBase_ Onclick (object sender, routedeventargs E) 2 {3 var ViewModel = this.datacontext as ViewModel; 4 task. Run (() = > 5 {6 / / this section calls exception 7 viewmodel.itemssource.add ("test1"); 8}); 9 }
- Under asynchronous thread, assign ObservableCollection – normal spa
private void ButtonBase_OnClick(object sender, RoutedEventArgs e) 2 { 3 var viewModel = this.DataContext as ViewModel; 4 Task.Run(() => 5 { 6 //No error will be reported in this paragraph 7 var list = viewModel.ItemsSource.ToList(); 8 list.Add("test0"); 9 viewModel.ItemsSource = new ObservableCollection<string>(list); 10 }); 11 }
- Under asynchronous thread, assign ObservableCollection and then modify ObservableCollection - normal
5. ` private void Button1_OnClick(object sender, RoutedEventArgs e) 2 { 3 var viewModel = this.DataContext as ViewModel; 4 Task.Run(() => 5 { 6 //No error will be reported in this paragraph 7 viewModel.ItemsSource = new ObservableCollection<string>(new List<string>() { "test3", "test2" }); 8 //No error will be reported in this paragraph 9 viewModel.ItemsSource.Add("test4"); 10 }); 11 }`
ItemsSource set under asynchronous thread can be called by UI thread. It can be understood here that when assigning values, the framework silently turns to the UI thread for processing? get
But why are the above three processes normal
- In the asynchronous thread, go back to the UI thread and modify ObservableCollection - normal
6. private void Button1_OnClick(object sender, RoutedEventArgs e) 2 { 3 var viewModel = this.DataContext as ViewModel; 4 Task.Run(() => 5 { 6 Application.Current.Dispatcher.Invoke(() => 7 { 8 //No error will be reported in this paragraph 9 viewModel.ItemsSource.Add("test"); 10 }); 11 }); 12 }
During WPF development, ObservableCollection needs to be operated from a new thread. As a result, the program throws an error ide of NotSupportedException
public class AsyncObservableCollection<T> : ObservableCollection<T> { //Gets the SynchronizationContext object of the current thread private SynchronizationContext _synchronizationContext = SynchronizationContext.Current; public AsyncObservableCollection() { } public AsyncObservableCollection(IEnumerable<T> list) : base(list) { } protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (SynchronizationContext.Current == _synchronizationContext) { //If the operation occurs in the same thread, cross thread execution is not necessary RaiseCollectionChanged(e); } else { //If it doesn't happen in the same thread //To be exact, this is the operation to update the UI in a non UI thread _synchronizationContext.Post(RaiseCollectionChanged, e); } } private void RaiseCollectionChanged(object param) { // implement base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param); } protected override void OnPropertyChanged(PropertyChangedEventArgs e) { if (SynchronizationContext.Current == _synchronizationContext) { // Execute the PropertyChanged event on the current thread RaisePropertyChanged(e); } else { // Post the PropertyChanged event on the creator thread _synchronizationContext.Post(RaisePropertyChanged, e); } } private void RaisePropertyChanged(object param) { // We are in the creator thread, call the base implementation directly base.OnPropertyChanged((PropertyChangedEventArgs)param); } }