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

Changing Map.TileLayer: change of behaviour from 1.11.3 to 2.4.0

Nov 20, 2014 at 12:15 PM
Since upgrading from v1.11.3 to v2.4.0 I have found that the map control no longer recognises a change to the TileLayer property but instead continues to render tiles from the existing TileLayer.

I have a ReadOnlyCollection<TileLayer> that is bound as the ItemsSource to a ComboBox control. The SelectedItem property of that ComboBox is bound to a property of type TileLayer in my view model and the Map.TileLayer property is bound to that same property within my view model.

In the previous version of the Map control, I was able to select alternate tile layers using the combo box and the map would be redrawn using the new tile layer. In the current version, I can see that the setter of the view model property is still being called when the value of the combo box changes, and this in turn is raising a PropertyChanged event that is followed by multiple (3)calls to the getter of the property but the tiles are not redrawn and even changing the viewport continues to use the original tile layer.
Coordinator
Nov 20, 2014 at 1:06 PM
Since you're upgrading from a very old version, it's hard to tell what's actually going wrong in your code. Please take a look at the current sample application to find out how tile layers are changed.
Nov 20, 2014 at 1:18 PM
I have just tried the previous release (2.3.1) and the functionality works without issue as it did in the earlier 1.11.3 (though that version still has the HttpWebClient.UserAgent issue so I cannot use it).

Your example application sets the Map.TileLayer property in code-behind using an event handler off of the ComboBox control, but basically it is the same with a new value being assigned to the Map.TileLayer property only I am using Bindings to achieve the same.

Did anything else change between 2.3.1 and 2.4.0?

Thanks.
Coordinator
Nov 20, 2014 at 2:00 PM
Edited Nov 20, 2014 at 2:00 PM
There were a lot of changes concerning the TileLayer handling in class MapBase. The problem here is that the TileLayer property is now always set when the TileLayers collection changes, which effectively removes your binding. I'm going to fix this in the next release. Meanwhile you could simply make your binding two-way. It should perhaps be two-way by default anyway, as then changes in the TileLayers property would be propagated back to the view model by default.
Nov 20, 2014 at 2:37 PM
I have tried making the binding (of Map.TileLayer) two way; it was originally one way only. Unfortunately, it has made no difference and the problem persists.

I will await your next updated version.

Thanks.
Nov 20, 2014 at 2:48 PM
Actually, thinking about it, I do not set the TileLayers property; I only set the TileLayer property.
Does this make a difference?
Coordinator
Nov 20, 2014 at 3:12 PM
I wasn't saying you should set the TileLayers property anywhere. Just making the TileLayer binding two-way should work. At least it does in my test program.
Nov 20, 2014 at 3:21 PM
Sorry, my misunderstanding.

My Map.TileLayer binding to the property on the view model was originally one way. My binding from the ComboxBox.SelectedItem to the property on the view model was two way. I have changed the Map.TileLayer binding to the property on the view model to be two way (which did not work with earlier versions) and it still makes no difference.

Would you be willing to send me your test program so that I can see if I can see what is happening? Unfortunately I can only go so far as to determine that the property on the view model is being set correctly (unless of course I build for myself).
Nov 20, 2014 at 3:43 PM
Edited Nov 20, 2014 at 4:14 PM
Thought I would make it easier; here is a simple test application I have put together myself in case I was missing something...

Starting with a default Wpf Application named MapApplication, add Xaml.MapControl using nuget and then...

MainWindow.xaml...
<Window x:Class="MapApplication.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:mapControl="clr-namespace:MapControl;assembly=MapControl.WPF"
                xmlns:v="clr-namespace:MapApplication"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                Title="MainWindow"
                Height="350"
                Width="525">

    <Window.DataContext>
        <v:MainWindowViewModel />
    </Window.DataContext>

    <DockPanel>

        <ComboBox DockPanel.Dock="Top"
                            DisplayMemberPath="SourceName"
                            ItemsSource="{Binding Path= TileLayers}"
                            SelectedValue="{Binding Path=TileLayer}"
                            ToolTip="Map tile source" />

        <mapControl:Map MinZoomLevel="4"
                            TileLayer="{Binding Path=TileLayer}" />

    </DockPanel>

</Window>
MainWindowViewModel.cs...
namespace MapApplication {

    #region Namespace references

    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using MapControl;

    #endregion

    public class MainWindowViewModel : INotifyPropertyChanged {

        #region Fields

        private TileLayer tileLayer;
        private ReadOnlyCollection<TileLayer> tileLayers;

        #endregion

        #region Constructor

        public MainWindowViewModel() {

            this.tileLayers = new ReadOnlyCollection<TileLayer>(new List<TileLayer> {

                new TileLayer {
                    SourceName = @"OpenStreetMap",
                    Description = @"© {y} OpenStreetMap Contributors, CC-BY-SA",
                    TileSource = new TileSource { UriFormat = @"http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png" },
                    MaxZoomLevel = 19
                },

                new TileLayer {
                    SourceName = @"Google Maps",
                    Description = @"Google Maps - © {y} Google",
                    TileSource = new TileSource { UriFormat = @"http://mt{i}.google.com/vt/x={x}&y={y}&z={z}" },
                    MaxZoomLevel = 20
                },

                new TileLayer {
                    SourceName = @"Google Images",
                    Description = @"Google Maps - © {y} Google",
                    TileSource = new TileSource { UriFormat = @"http://khm{i}.google.com/kh/v=144&x={x}&y={y}&z={z}" },
                    MaxZoomLevel = 20
                },

                new TileLayer {
                    SourceName = @"Bing Maps",
                    Description = @"Bing Maps - © {y} Microsoft Corporation",
                    TileSource = new TileSource { UriFormat = @"http://ecn.t{i}.tiles.virtualearth.net/tiles/r{q}.png?g=0&stl=h" },
                    MaxZoomLevel = 19
                },

                new TileLayer {
                    SourceName = @"Bing Images",
                    Description = @"Bing Maps - © {y} Microsoft Corporation",
                    TileSource = new TileSource { UriFormat = @"http://ecn.t{i}.tiles.virtualearth.net/tiles/a{q}.jpeg?g=0" },
                    MaxZoomLevel = 19
                },

                new TileLayer {
                    SourceName = @"Bing Hybrid",
                    Description = @"Bing Maps - © {y} Microsoft Corporation",
                    TileSource = new TileSource { UriFormat = @"http://ecn.t{i}.tiles.virtualearth.net/tiles/h{q}.jpeg?g=0&stl=h" },
                    MaxZoomLevel = 19
                }

            });

            this.TileLayer = this.TileLayers.First();

        }

        #endregion

        #region Properties & events

        public event PropertyChangedEventHandler PropertyChanged;

        public TileLayer TileLayer {
            get { return this.tileLayer; }
            set {
                if (this.tileLayer != value) {
                    this.tileLayer = value;
                    this.RaisePropertyChanged(@"TileLayer");
                }
            }
        }

        public IList<TileLayer> TileLayers {
            get { return this.tileLayers; }
        }

        #endregion

        #region Methods

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {

            PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if (propertyChanged != null)
                propertyChanged(this, e);

        }

        protected virtual void RaisePropertyChanged(string propertyName) {

            this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));

        }

        #endregion

    }

}
I am looking for the map view to change when I change the selection in the ComboBox. This works if you build it against version 2.3.1 of your map control, but does not work if you build it against 2.4.0

Martin.
Coordinator
Nov 20, 2014 at 4:21 PM
Please check out version 2.4.1.
Marked as answer by MartinRobins on 11/20/2014 at 8:30 AM
Nov 20, 2014 at 4:30 PM
Oh how I wish everybody that I worked with over the years could have been as helpful as you have been.
Version 2.4.1 restores the functionality (as I am sure you knew it would) and I am a very happy bunny.

Again, thank you for your consideration.