This project has moved. For the latest updates, please go here.

Need for MapControl.Location

Jan 5, 2015 at 9:43 AM
Hi,

I'm trying to cleanly resolve the case in my application, where I have a complex model structure with various regions (polylines with GPS coordinates) and telemetry-data (again GPS coordinates with various other information) grouped into flight stages and mission segments. I want to display this information on the map, but it always expects it's own Location class. I would like to make it bind to the Latitude and Longitude properties in my Models instead. Otherwise I'd have to extract all those collections from my models into the ViewModel and handle manipulations back into the Models by hand.

or does someone know a cleaner way of approaching this?

regards, Tilli
Coordinator
Jan 5, 2015 at 1:37 PM
Use a binding converter.
Jan 5, 2015 at 3:41 PM
Hi Clemens,

a Converter has multiple drawbacks:
  • converting every element in a collection is not directly supported
  • if you convert the complete collection, the original collection is not bound anymore, so any changes to it are not reflected in your on-the-fly collection created during conversion
  • all changes made in the UI are only reflected in the ad-hoc collection, not in the originating collection. of course I could handle all related events but that is rather cumbersome
at least I have not found a suitable converter implementation which takes care of these drawbacks
Jan 6, 2015 at 6:50 PM
I've created a minimal invasive extension of MapPolyline for testing and it works and allows binding Lat/Lon from arbitrary objects. Since it's the first time I've used Bindings in this way, maybe you could comment the code? I'm using WPF binding (to a dummy object) to retrieve the actual Lat/Lon values and transform them into your required Location class.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace ...
{
    public class ExtendedMapPolyline : MapControl.MapPolyline
    {

        public static DependencyProperty ItemsSourceProperty = DependencyProperty
            .Register("ItemsSource", typeof(IEnumerable), typeof(ExtendedMapPolyline)
                , new FrameworkPropertyMetadata(null
                    , new PropertyChangedCallback(OnItemsSourceChanged)
                ));

        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public static DependencyProperty LatitudeMemberPathProperty = DependencyProperty
            .Register("LatitudeMemberPath", typeof(Binding), typeof(ExtendedMapPolyline)
                , new FrameworkPropertyMetadata(new Binding("Latitude")
                    , new PropertyChangedCallback(OnMemberPathChanged)
                ));

        public Binding LatitudeMemberPath
        {
            get { return (Binding)GetValue(LatitudeMemberPathProperty); }
            set { SetValue(LatitudeMemberPathProperty, value); }
        }

        public static DependencyProperty LongitudeMemberPathProperty = DependencyProperty
            .Register("LongitudeMemberPath", typeof(Binding), typeof(ExtendedMapPolyline)
                , new FrameworkPropertyMetadata(new Binding("Longitude")
                    , new PropertyChangedCallback(OnMemberPathChanged)
                ));

        public Binding LongitudeMemberPath
        {
            get { return (Binding)GetValue(LongitudeMemberPathProperty); }
            set { SetValue(LongitudeMemberPathProperty, value); }
        }

        private ObservableCollection<ExtendedLocation> InnerLocations
        {
            get { return base.Locations as ObservableCollection<ExtendedLocation>; }
            set { base.Locations = value; }
        }

        private static void OnMemberPathChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var mapPolyline = (ExtendedMapPolyline)obj;
            mapPolyline.InnerLocations = new ObservableCollection<ExtendedLocation>();
            if (mapPolyline.ItemsSource != null)
                mapPolyline.ProcessAllItems(mapPolyline.ItemsSource);
        }

        private static void OnItemsSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var mapPolyline = (ExtendedMapPolyline)obj;
            var oldCollection = e.OldValue as INotifyCollectionChanged;
            var newCollection = e.NewValue as INotifyCollectionChanged;

            if (oldCollection != null)
            {
                oldCollection.CollectionChanged -= mapPolyline.OnItemsSourceCollectionChanged;
            }

            if (newCollection != null)
            {
                newCollection.CollectionChanged += mapPolyline.OnItemsSourceCollectionChanged;
            }

            mapPolyline.InnerLocations = new ObservableCollection<ExtendedLocation>();
            if (newCollection != null)
                mapPolyline.ProcessAllItems((IEnumerable)newCollection);
        }

        private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (var i in e.NewItems)
                        ProcessNewItem(i);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (var i in e.NewItems)
                        RemoveItem(i);
                    break;
                //case NotifyCollectionChangedAction.Move:
                //case NotifyCollectionChangedAction.Replace:
                //case NotifyCollectionChangedAction.Reset:
                //default:
                //  break;
            }
        }

        private void ProcessAllItems(IEnumerable items)
        {
            foreach (var item in items)
            {
                ProcessNewItem(item);
            }
        }

        private void ProcessNewItem(object i)
        {
            Exception ex;
            ApplicationHelper.ExecuteOnUIThread(() =>{
            lock (LatitudeMemberPath)
            {
                var binding = (Binding)LatitudeMemberPath.Clone();
                binding.Source = i;
                var dummy = new Dummy();
                dummy.SetBinding(Dummy.LatitudeProperty, binding);
                binding = (Binding)LongitudeMemberPath.Clone();
                binding.Source = i;
                dummy.SetBinding(Dummy.LongitudeProperty, binding);
                var loc = new ExtendedLocation(dummy.Latitude, dummy.Longitude, i);
                BindingOperations.ClearAllBindings(dummy);
                dummy = null;
                InnerLocations.Add(loc);
            }
            }, out ex);
            if (ex != null)
              throw ex;
        }

        private void RemoveItem(object item)
        {
            var loc = InnerLocations.FirstOrDefault(l => item == l.Source);
            if (loc != null)
                InnerLocations.Remove(loc);
        }

        private class ExtendedLocation : MapControl.Location
        {
            public ExtendedLocation(double lat, double lon, object source)
                : base(lat, lon)
            {
                Source = source;
            }
            public object Source { get; set; }
        }

        private class Dummy : FrameworkElement
        {

            public static DependencyProperty LatitudeProperty = DependencyProperty
            .Register("Latitude", typeof(double), typeof(Dummy)
                , new FrameworkPropertyMetadata(0d
                //, new PropertyChangedCallback(OnLatitudeChanged)
                ));

            public double Latitude
            {
                get { return (double)GetValue(LatitudeProperty); }
                set { SetValue(LatitudeProperty, value); }
            }

            public static DependencyProperty LongitudeProperty = DependencyProperty
            .Register("Longitude", typeof(double), typeof(Dummy)
                , new FrameworkPropertyMetadata(0d
                //, new PropertyChangedCallback(OnLongitudeChanged)
                ));

            public double Longitude
            {
                get { return (double)GetValue(LongitudeProperty); }
                set { SetValue(LongitudeProperty, value); }
            }

        }

    }
}

Marked as answer by ClemensF on 1/27/2015 at 1:16 PM