You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在参考点变更时刷新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 11:07:10