I have a Datagrid
containing addresses, their geo-coordinate and the distance to a reference point. The reference point is in a global variable and the distance is calculated on the fly.
The following is the data model of an item which is placed in an ObservableCollection
:
public class Address : INotifyPropertyChanged
{
// like "175 5th Ave, New York, NY 10010"
public string location {
get {
return _location;
}
set {
if( value != _location ) {
_location = value;
NotifyPropertyChanged();
}
}
}
// encapsulates latitude and longitude
public GeoCoordinate? Coordinate {
get {
return _coordinate;
}
set {
if( !value.Equals( _coordinate )) {
_coordinate = value;
NotifyPropertyChanged();
NotifyPropertyChanged( "Distance" );
}
}
}
// Calculates the distance between the reference point and this item
[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 ) );
}
}
}
This works well when I change the coordinate of an item. The distance in the DataGrid
will be updated accordingly.
However, when I change the SelectedReferencePoint
, the distance won't be updated, obviously. I am wondering what would be the best way to do it.
Of course I can loop through all items in the collection and call NotifyPropertyChanged( "Distance" )
manually. However, is there a better way to tell the DataGrid
to recalculate the contents of the Distance
column?
One solution as commented would be to subscribe to change notifications from within the Address
when Globals.SelectedReferencePoint
changes value. Then fire the Address.PropertyChanged
on the Distance
property when that happens:
public class Globals : INotifyPropertyChanged
{
public static Globals Instance = /* ... */;
public GeoCoordinate _refPoint;
public GeoCoordinate SelectedReferencePoint
{
get
{
return _refPoint;
}
set
{
if(!value.Equals(_refPoint))
{
_refPoint = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if(PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
public class Address : INotifyPropertyChanged
{
// Take Globals as input (= code is easier to test)
// or just access Globals.Instance instead
public Address(Globals globals)
{
globals.PropertyChanged += HandleGlobalsPropertyChanged;
}
private void HandleGlobalsPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Globals.SelectedReferencePoint))
{
NotifyPropertyChanged(nameof(Distance));
}
}
// ...
}
Note: remember to unsubscribe from Globals.PropertyChanged
again if an Address
has a shorter lifespan than Globals
.