none
Win 8.1: MapShapeLayer: Shape is already a member of a MapShapeLayer RRS feed

  • Question

  • In my Win 8.1/VS2013 Windows Store App, the user can make different queries. After the first query, the code below always works. The second or third query might result in the following error:

    Shape is already a member of a MapShapeLayer

    In the code below, _polylineLayer is a MapShapeLayer object. Clearly, the Contains/Remove call isn't always (or ever) working.

    For debugging, I also tried _polylineLayer.Shapes.Clear(). I still get the Shap is already a member of a MapShapeLayer.

    What is the correct way to clear a MapShapeLayer object for reuse?

    Thanks...

    foreach (Bing.Maps.MapPolyline mapPolyline in container.MyPolylines)
    {
        if (_polylineLayer.Shapes.Contains(mapPolyline))
            _polylineLayer.Shapes.Remove(mapPolyline);
                                    
        _polylineLayer.Shapes.Add(mapPolyline);
    }


    Randy

    Thursday, March 13, 2014 7:29 PM

Answers

  • This has nothing to do with Bing Maps and actually how Windows (along with WPF and Silverlight) are designed. Since the object has already been added to the view, even though it has been removed you can't add it again. Here is a simple example that reproduces this restriction:

    <Page
        x:Class="TestApp.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:TestApp"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <StackPanel>
            <Button Content="Add" Click="AddBtn_Tapped"/>
    
            <Grid Name="Container"/>
        </StackPanel>
    </Page>
    
    using Windows.UI;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Shapes;
    
    namespace TestApp
    {
        public sealed partial class MainPage : Page
        {
            private Ellipse item;
    
            public MainPage()
            {
                this.InitializeComponent();
    
                item = new Ellipse()
                {
                    Width = 400,
                    Height = 200,
                    Fill = new SolidColorBrush(Colors.Red)
                };
            }
    
            private void AddBtn_Tapped(object sender, RoutedEventArgs e)
            {
                if (Container.Children.Contains(item))
                {
                    Container.Children.Add(item);
                }
    
                Container.Children.Add(item);
            }
        }
    }
    

    The first time you click the button a read ellipse will be displayed on the screen. The second time you press it the app will throw an error.


    http://rbrundritt.wordpress.com

    Friday, March 14, 2014 5:37 PM
  • There are several ways to handle this situation. The most common is to simply clear the layer and add the new data.

    Another method is to store metadata of the locations in the Tag property of the Pushpins. Loop through the result data and add any that are not already on the map based on the metadata. This requires more work in development and also more work in process so Is not normally done, but is one way to grow the amount of data that is on the map.

    The first method makes the most sense as is ensures a fairly consistent amount of memory usage by the app, is easy to develop, and gives pretty much the same experience as the more complex method.


    http://rbrundritt.wordpress.com

    Monday, March 17, 2014 10:34 AM
  • Where are you getting your shapes from? They should be created uniquely for each result. What you should do is loop through each result, create a polygon/polyline for it an add it to the shape layer.

    http://rbrundritt.wordpress.com

    Monday, March 17, 2014 12:55 PM
  • It seems I've come up with a solution that works. If I keep a savedContainer of the previous query, and remove all the Shapes item-by-item from the savedContainer before adding items from the latest query, then I no longer get the error: Shape is already a member of a MapShapeLayer:

    foreach (Bing.Maps.MapPolyline mapPolyline in savedContainer.AdvisoryPolylines)
    {
        _polylineLayer.Shapes.Remove(mapPolyline);
    }
    
    foreach (Bing.Maps.MapPolygon mapPolygon in savedContainer.AdvisoryPolygons)
    {
        _polygonLayer.Shapes.Remove(mapPolygon);
    }
    Being able to talk with someone about this probably saved me days, so thanks! For the record, I'm using Bing Maps for C#, C++, or Visual Basic, version 1.313.825.0. My guess is that the Clear() method will be updated in some future release.


    Randy

    Monday, March 17, 2014 2:24 PM

All replies

  • This has nothing to do with Bing Maps and actually how Windows (along with WPF and Silverlight) are designed. Since the object has already been added to the view, even though it has been removed you can't add it again. Here is a simple example that reproduces this restriction:

    <Page
        x:Class="TestApp.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:TestApp"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <StackPanel>
            <Button Content="Add" Click="AddBtn_Tapped"/>
    
            <Grid Name="Container"/>
        </StackPanel>
    </Page>
    
    using Windows.UI;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Shapes;
    
    namespace TestApp
    {
        public sealed partial class MainPage : Page
        {
            private Ellipse item;
    
            public MainPage()
            {
                this.InitializeComponent();
    
                item = new Ellipse()
                {
                    Width = 400,
                    Height = 200,
                    Fill = new SolidColorBrush(Colors.Red)
                };
            }
    
            private void AddBtn_Tapped(object sender, RoutedEventArgs e)
            {
                if (Container.Children.Contains(item))
                {
                    Container.Children.Add(item);
                }
    
                Container.Children.Add(item);
            }
        }
    }
    

    The first time you click the button a read ellipse will be displayed on the screen. The second time you press it the app will throw an error.


    http://rbrundritt.wordpress.com

    Friday, March 14, 2014 5:37 PM
  • I appreciate the response. Still, my workflow is something like the following:

    Say that the user has a critical need to plot coffee shops based on specific parameters (I'm not actually working with coffee shops, but this seems to be the familiar example). So the user performs a query and gets a list of coffee shop objects that include center-point and radius (the bigger the radius, the bigger the coffee shop). Right now, I have the XAML code setup so that every time the list is updated, the coffee shops in the list are plotted on my Bing Map.

    It would be legitimate in my scenario for the user to perform dozens or hundreds of queries, based on region, size, hours of operations, etc. Some of these queries would return a subset of the previous query, and some a superset.

    I doubt I'm the first person to run into this scenario. Are people deleting the MapLayerObject and forcing Garbage Collection (typically considered a bad thing to do). Are people just adding a new layer for every new query and hoping they don't run out of memory (also not good)?

    In order of Bing Maps to be viable for anything beyond a quick demo, there has to be a architecturally sound approach to this.

    All thoughts related to solving this problem are welcome.

    Thanks...



    Randy

    Sunday, March 16, 2014 11:14 AM
  • There are several ways to handle this situation. The most common is to simply clear the layer and add the new data.

    Another method is to store metadata of the locations in the Tag property of the Pushpins. Loop through the result data and add any that are not already on the map based on the metadata. This requires more work in development and also more work in process so Is not normally done, but is one way to grow the amount of data that is on the map.

    The first method makes the most sense as is ensures a fairly consistent amount of memory usage by the app, is easy to develop, and gives pretty much the same experience as the more complex method.


    http://rbrundritt.wordpress.com

    Monday, March 17, 2014 10:34 AM
  • Clearing the data and then reading the results of the latest query sounds good. I've modified my code as shown below, but I still get the following error when I do the a second query, even though the Clear() method sets the number of Shapes to zero.

    So the real question is: how do I clear the layer? I don't see a MapShapeLayer.Clear() API in my Win8.1/VS2013/Bing Map environment.

    Thanks...

    _polylineLayer.Shapes.Clear();
    foreach (Bing.Maps.MapPolyline mapPolyline in container.AdvisoryPolylines)
    {
        _polylineLayer.Shapes.Add(mapPolyline);
    }
    
    _polygonLayer.Shapes.Clear();
    foreach (Bing.Maps.MapPolygon mapPolygon in container.AdvisoryPolygons)
    {
        _polygonLayer.Shapes.Add(mapPolygon);
    }


    Randy

    Monday, March 17, 2014 12:24 PM
  • Where are you getting your shapes from? They should be created uniquely for each result. What you should do is loop through each result, create a polygon/polyline for it an add it to the shape layer.

    http://rbrundritt.wordpress.com

    Monday, March 17, 2014 12:55 PM
  • I get a response from a web service, and that response contains a lat/lng center point, radius, and units of measure for the radius. Using geometry, I calculate the points for each 1 degree arc through 360 degrees, and these make up the circles I draw on the map as a single MapPolyline object. I needed to do it this way because I needed the circles to scale with the map when the user zooms in and out.

    In my constructor, I link the MapShapeLayer objects to the Bing Map itself. I've modified the code as shown below to clear the Map object ShapeLayer object, then re-add the PolyLine and Polygon layer. I still get the "Shape is already a member of a MapShapeLayer" exception.

    All thoughts are appreciated.

    _polylineLayer.Shapes.Clear();
    _polygonLayer.Shapes.Clear();
    
    _myMap.ShapeLayers.Clear();
                        
    _myMap.ShapeLayers.Add(_polylineLayer);
    _myMap.ShapeLayers.Add(_polygonLayer);
    
    foreach (ResultContainer container in _aeroAdvisoryQueriesVM.ResultContainers)
    {
                
        foreach (Bing.Maps.MapPolyline mapPolyline in container.AdvisoryPolylines)
        {
            _polylineLayer.Shapes.Add(mapPolyline);
        }
    
                            
        foreach (Bing.Maps.MapPolygon mapPolygon in container.AdvisoryPolygons)
        {
            _polygonLayer.Shapes.Add(mapPolygon);
        }
    }


    Randy


    Monday, March 17, 2014 1:17 PM
  • It seems I've come up with a solution that works. If I keep a savedContainer of the previous query, and remove all the Shapes item-by-item from the savedContainer before adding items from the latest query, then I no longer get the error: Shape is already a member of a MapShapeLayer:

    foreach (Bing.Maps.MapPolyline mapPolyline in savedContainer.AdvisoryPolylines)
    {
        _polylineLayer.Shapes.Remove(mapPolyline);
    }
    
    foreach (Bing.Maps.MapPolygon mapPolygon in savedContainer.AdvisoryPolygons)
    {
        _polygonLayer.Shapes.Remove(mapPolygon);
    }
    Being able to talk with someone about this probably saved me days, so thanks! For the record, I'm using Bing Maps for C#, C++, or Visual Basic, version 1.313.825.0. My guess is that the Clear() method will be updated in some future release.


    Randy

    Monday, March 17, 2014 2:24 PM
  • Glad you found a solution. Personally I would of created a custom circle class which could be added to the map using a technique similar to what I've done in this code sample. From there I would remove all shapes and add only the new one's by creating new Circle objects each time. Alternatively, I you could reuse the circles by updating the center and radius of the circles and add/remove additional/remaining circles accordingly.

    http://rbrundritt.wordpress.com

    Monday, March 17, 2014 4:49 PM