The difference between ObservableCollection and List and the asynchronous call of ObservableCollection

Posted by Wardy7 on Mon, 31 Jan 2022 03:26:12 +0100

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

  1. Modify ObservableCollection directly under asynchronous thread - report this error
  2. 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 }
  3. 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     }
  1. 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

  1. 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);
    }
}

Topics: WPF