如何在参考点变更时刷新DataGrid的Distance列数据?
问题描述
我有一个包含地址、地理坐标及到参考点距离的DataGrid,参考点存储在全局变量中,距离为实时计算值。存入ObservableCollection的数据模型如下:
public class Address : INotifyPropertyChanged { // 示例:"175 5th Ave, New York, NY 10010" public string location { get { return _location; } set { if( value != _location ) { _location = value; NotifyPropertyChanged(); } } } // 封装纬度和经度 public GeoCoordinate? Coordinate { get { return _coordinate; } set { if( !value.Equals( _coordinate )) { _coordinate = value; NotifyPropertyChanged(); NotifyPropertyChanged( "Distance" ); } } } // 计算当前项与参考点的距离 [JsonIgnore] public int? Distance { get { if( Coordinate == null || Globals.Instance.SelectedReferencePoint == null ) return null; var rp = Globals.Instance.SelectedReferencePoint; return ( int ) ( Math.Round( rp.Coordinate.DistanceTo( ( GeoCoordinate ) Coordinate ) / 1000.0 ) + 0.1 ); } } private string _location; private GeoCoordinate? _coordinate; public event PropertyChangedEventHandler? PropertyChanged; private void NotifyPropertyChanged( [CallerMemberName] String propertyName = "" ) { if( PropertyChanged != null ) { PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) ); } } }
当前修改项的坐标时,DataGrid中的距离会相应更新,但修改SelectedReferencePoint时距离不会更新。希望找到最优解决方案,除了遍历集合手动调用NotifyPropertyChanged("Distance"),有没有更好的方法让DataGrid重新计算Distance列的内容?
最优解决方案
1. 让全局参考点支持属性变更通知
首先修改Globals类,让SelectedReferencePoint属性能触发PropertyChanged事件,这样它变化时能被其他对象监听:
public class Globals : INotifyPropertyChanged { private static readonly Globals _instance = new Globals(); public static Globals Instance => _instance; private ReferencePoint? _selectedReferencePoint; public ReferencePoint? SelectedReferencePoint { get => _selectedReferencePoint; set { if (_selectedReferencePoint != value) { _selectedReferencePoint = value; NotifyPropertyChanged(); } } } public event PropertyChangedEventHandler? PropertyChanged; private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } // 注意:如果ReferencePoint的坐标也可能变动,它自身也要实现INotifyPropertyChanged public class ReferencePoint : INotifyPropertyChanged { private GeoCoordinate? _coordinate; public GeoCoordinate? Coordinate { get => _coordinate; set { if (!Equals(_coordinate, value)) { _coordinate = value; NotifyPropertyChanged(); } } } public event PropertyChangedEventHandler? PropertyChanged; private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 在Address类中监听全局参考点的变化
让每个Address实例感知SelectedReferencePoint的变化,进而触发自身Distance属性的更新,这里有两种实现方式:
方式一:直接订阅(简单但需手动清理)
给Address添加构造函数订阅Globals的事件,同时实现IDisposable取消订阅,避免内存泄漏:
public class Address : INotifyPropertyChanged, IDisposable { public Address() { Globals.Instance.PropertyChanged += OnGlobalPropertyChanged; } private void OnGlobalPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(Globals.SelectedReferencePoint)) { NotifyPropertyChanged(nameof(Distance)); } } public void Dispose() { Globals.Instance.PropertyChanged -= OnGlobalPropertyChanged; } // 原有的location、Coordinate属性和NotifyPropertyChanged代码保持不变... }
方式二:用弱事件管理器(更省心,无需手动取消)
如果不想手动管理订阅生命周期,使用WeakEventManager可以自动处理内存回收,不用写Dispose逻辑:
public class Address : INotifyPropertyChanged { public Address() { WeakEventManager<Globals, PropertyChangedEventArgs>.AddHandler( Globals.Instance, nameof(Globals.PropertyChanged), OnGlobalPropertyChanged); } private void OnGlobalPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(Globals.SelectedReferencePoint)) { NotifyPropertyChanged(nameof(Distance)); } } // 原有的属性和NotifyPropertyChanged代码保持不变... }
3. 额外处理:如果参考点的坐标也会变动
要是ReferencePoint的Coordinate可能被修改,还需要监听它的变化,否则坐标更新后距离不会同步。修改OnGlobalPropertyChanged方法:
private ReferencePoint? _currentReferencePoint; private void OnGlobalPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(Globals.SelectedReferencePoint)) { // 取消旧参考点的监听 if (_currentReferencePoint != null) { WeakEventManager<ReferencePoint, PropertyChangedEventArgs>.RemoveHandler( _currentReferencePoint, nameof(ReferencePoint.PropertyChanged), OnReferencePointPropertyChanged); } // 监听新参考点 _currentReferencePoint = Globals.Instance.SelectedReferencePoint; if (_currentReferencePoint != null) { WeakEventManager<ReferencePoint, PropertyChangedEventArgs>.AddHandler( _currentReferencePoint, nameof(ReferencePoint.PropertyChanged), OnReferencePointPropertyChanged); } // 触发距离更新 NotifyPropertyChanged(nameof(Distance)); } } private void OnReferencePointPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(ReferencePoint.Coordinate)) { NotifyPropertyChanged(nameof(Distance)); } }
方案优势
- 无需遍历整个集合,每个Address实例独立响应变化,性能更高效。
- 完全依托WPF绑定机制自动更新UI,符合MVVM设计原则。
- 弱事件方式避免了单例全局对象导致的内存泄漏问题,长期运行更稳定。
内容的提问来源于stack exchange,提问作者Florian Kusche
相关产品推荐
相关产品推荐

