应使用 observablecollection 替代 list 实现 ui 刷新,因其实现 inotifycollectionchanged 接口并触发 collectionchanged 事件;元素属性变更需模型类实现 inotifypropertychanged;多线程修改须通过 dispatcher 切回 ui 线程。

WPF 中绑定后集合增删不刷新 UI,基本就是用了 List<t></t> 而不是 ObservableCollection<t></t> ——这不是 bug,是机制决定的。
为什么 ListBox.ItemsSource 绑定 List<t></t> 后调用 Add() 没反应
WPF 的 ItemsControl(如 ListBox、DataGrid)只监听实现了 INotifyCollectionChanged 接口的集合变动。List<t></t> 完全不触发任何事件,UI 线程收不到通知,自然不会重绘。
-
ObservableCollection<t></t>内置CollectionChanged事件,每次Add()、Remove()、Clear()都会触发 - 它继承自
Collection<t></t>,支持所有常规操作,但必须走它自己的方法(比如不能用myList[i] = x替代myObsc.SetItem(i, x)) - 构造时可传入
IEnumerable<t></t>初始化,但后续修改仍需调用ObservableCollection<t></t>提供的方法才能通知 UI
ObservableCollection<t></t> 不监听元素内部属性变化
它只报告“集合结构变化”(项增、删、移位、重置),不关心里面每个对象的字段有没有改。比如你绑了一个 ObservableCollection<person></person> 到 DataGrid,然后执行 person.Name = "new",单元格内容不会自动更新。
- 要让 UI 响应元素属性变更,
Person类必须实现INotifyPropertyChanged - 常见错误:只在 ViewModel 层用了
ObservableCollection<t></t>,却忘了模型类本身没通知机制 - 如果只是展示静态数据(如枚举项列表),且确定永不修改单个属性,可以省略
INotifyPropertyChanged
多线程直接修改 ObservableCollection<t></t> 会崩
它本身不是线程安全的。在非 UI 线程里调用 Add() 或 Remove(),大概率抛 InvalidOperationException:“集合已更改;枚举操作可能无法执行” 或 “调用线程无法访问此对象”。
- 正确做法:用
Dispatcher.Invoke()或await Dispatcher.InvokeAsync()把操作切回 UI 线程 - 不要试图加锁绕过——
CollectionChanged事件回调默认在 UI 线程执行,锁住集合反而可能卡死 UI - 高频批量写入场景(如实时日志流),考虑先在后台线程攒一批数据,再一次性
AddRange()(需自行封装或用第三方扩展)
嵌套集合绑定时,子集合也要是 ObservableCollection<t></t>
比如父模型有 public ObservableCollection<plc> PLCs { get; }</plc>,而 PLC 类里又有 public ObservableCollection<tag> Tags { get; }</tag>,那么点击某个 PLC 后右侧 ListView 要显示其 Tags,这个 Tags 属性也必须是 ObservableCollection<tag></tag>。
- XAML 中用
{Binding Tags}就能自动识别并监听子集合变化 - 如果子集合用的是
List<tag></tag>,即使父级是ObservableCollection<plc></plc>,子视图也不会响应Tags.Add() - 注意:父级
PLC类自身若要响应Name修改,仍需实现INotifyPropertyChanged
最容易被忽略的一点:ObservableCollection<t></t> 是为 WPF 设计的,依赖 WindowsBase 程序集;.NET Core/.NET 5+ 项目需确认已引用该程序集,否则编译报错找不到类型。











