問題介紹
當ObservableCollection清單被UI線程占用時,如果在異步線程中調用ObservableCollection,會彈出以下異常:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SMxcTN0IDN2czNwYjZ4czYyYzX2UDO1kDM4AzLcdDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
問題分析
我們使用一個viewModel,在ViewModel中添加ObservableCollection類型的ItemsSource清單。
在清單使用ListBox綁定ItemsSource清單。再由界面觸發對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. 直接在異步線程下修改ObservableCollection--報錯
1 private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
2 {
3 var viewModel = this.DataContext as ViewModel;
4 Task.Run(() =>
5 {
6 //此段調用異常
7 viewModel.ItemsSource.Add("test1");
8 });
9
2. 在異步線程下,指派ObservableCollection--正常
1 private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
2 {
3 var viewModel = this.DataContext as ViewModel;
4 Task.Run(() =>
5 {
6 //此段不會報錯
7 var list = viewModel.ItemsSource.ToList();
8 list.Add("test0");
9 viewModel.ItemsSource = new ObservableCollection<string>(list);
10 });
11
3. 在異步線程下,指派ObservableCollection後,再修改ObservableCollection--正常
1 private void Button1_OnClick(object sender, RoutedEventArgs e)
2 {
3 var viewModel = this.DataContext as ViewModel;
4 Task.Run(() =>
5 {
6 //此段不會報錯
7 viewModel.ItemsSource = new ObservableCollection<string>(new List<string>() { "test3", "test2" });
8 //此段不會報錯
9 viewModel.ItemsSource.Add("test4");
10 });
11
在異步線程下設定的ItemsSource,可以被目前異步線程調用。
4. 異步線程下指派ObservableCollection,然後在UI線程修改ObservableCollection--正常
1 private void Button1_OnClick(object sender, RoutedEventArgs e)
2 {
3 var viewModel = this.DataContext as ViewModel;
4 Task.Run(() =>
5 {
6 //此段不會報錯
7 viewModel.ItemsSource = new ObservableCollection<string>(new List<string>() { "test0" });
8 });
9 }
10
11
12 private void Button2_OnClick(object sender, RoutedEventArgs e)
13 {
14 var viewModel = this.DataContext as ViewModel;
15 //此段不會報錯
16 viewModel.ItemsSource.Add("test2");
17
在異步線程下設定的ItemsSource,可以被UI線程調用。此處可以了解為,指派時,架構默默轉到UI線程處理了?
但是上面3流程,為何正常,so weird~
5. 異步線程下,回到UI線程中,修改ObservableCollection--正常
1 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 //此段不會報錯
9 viewModel.ItemsSource.Add("test");
10 });
11 });
12