在异步Task中使用ObservableCollection的AddRange方法时遭遇NotSupportedException
解决异步Task中操作ObservableCollection的AddRange抛出NotSupportedException的问题
兄弟,你碰到的这个问题核心原因其实很明确,咱们拆解来看:
首先,.NET默认的ObservableCollection<T>根本没有内置的AddRange方法——如果你是自己写了扩展方法实现这个功能,那问题的关键不在方法本身,而是你在Task.Run开启的**后台线程(非UI线程)**里直接修改了绑定到UI的集合。ObservableCollection是线程不安全的,它只能在创建它的线程(也就是UI线程)上进行修改操作,跨线程修改就会抛出NotSupportedException。
另外你更新Status属性的操作如果是绑定到UI的,同样要注意必须在UI线程执行,否则也可能引发绑定异常。
下面给你两种常见UI框架下的解决方案:
方案1:WPF/Windows Forms场景
用Dispatcher将操作调度回UI线程:
private ObservableCollection<CoronavirusCountry> _data = new ObservableCollection<CoronavirusCountry>(); public ObservableCollection<CoronavirusCountry> data { get => _data; set => SetProperty(ref _data, value); } Task.Run(async()=>{ APIService service = new APIService(); // 先在后台线程完成数据请求,避免阻塞UI var newData = await service.GetTopCases(); // 切换到UI线程更新集合和状态 Application.Current.Dispatcher.Invoke(() => { data.AddRange(newData); Status = "Updated " + DateTime.Now; }); });
方案2:MAUI/Blazor WebAssembly场景
用MainThread切换到UI线程:
private ObservableCollection<CoronavirusCountry> _data = new ObservableCollection<CoronavirusCountry>(); public ObservableCollection<CoronavirusCountry> data { get => _data; set => SetProperty(ref _data, value); } Task.Run(async()=>{ APIService service = new APIService(); var newData = await service.GetTopCases(); // 异步调度到UI线程执行更新 await MainThread.InvokeOnMainThreadAsync(() => { data.AddRange(newData); Status = "Updated " + DateTime.Now; }); });
补充:如果你的AddRange是自定义扩展方法
记得要实现高效的集合变更通知(一次性触发事件,而非循环触发单个Add的通知),示例如下:
public static void AddRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items) { if (collection == null) throw new ArgumentNullException(nameof(collection)); if (items == null) throw new ArgumentNullException(nameof(items)); // 先批量添加到内部集合 foreach (var item in items) { collection.Items.Add(item); } // 一次性触发集合变更通知,减少UI刷新次数 collection.TriggerCollectionChanged(items.ToList()); } // 辅助方法:调用ObservableCollection的保护方法触发事件 private static void TriggerCollectionChanged<T>(this ObservableCollection<T> collection, List<T> addedItems) { var eventHandler = typeof(ObservableCollection<T>) .GetProperty("CollectionChanged", BindingFlags.NonPublic | BindingFlags.Instance)? .GetValue(collection) as NotifyCollectionChangedEventHandler; eventHandler?.Invoke(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, addedItems)); }
最后再提醒一句:所有绑定到UI的属性、集合的修改操作,都必须在UI线程执行,这是UI框架的基本线程安全规则,别偷懒哦😉
内容的提问来源于stack exchange,提问作者Atena
相关产品推荐
相关产品推荐

