Inverse Font Colour on MapPinPoint

6 posts, 0 answers
  1. Stuart
    Stuart avatar
    8 posts
    Member since:
    May 2011

    Posted 28 Jul 2011 Link to this post

    Hi Guys,

    I have a WPF app which has a RadMap with 2 layers. The first layer is a dynamic layer which contains lots of MapPolygons loaded from shape files with the background colour set according to the metadata in the shape file. The second layer is an information layer with lots of MapPinPoints which sit on top of the shapes in the dynamic layer.

    This app is going onto a ruggadised tablet and will be used in direct sunlight, and as such the users would like a good contrast between the text on the MapPinPoints and the MapPolygons they sit on top of. I've written the code to change the fonts and this works really well when I can get it to execute. The problem I have is trying to figure out when the dynamic layer has finished rendering so that it's .Items property actually has something in it and I can check which polygons a pinpoint falls into.
     
    I have tried hooking into various events such as LayoutUpdated, SizeChanged, etc.. I even created my own event on the MapDynamicSource to notify me when the ItemRequest is complete, but the Items collection is still empty at this point...

    Both layers change fairly regularly and so the Loaded event doesn't help me either because it only fires once.

    If you need any more info please let me know, otherwise any help would be really appreciated.

    Thanks,
    Stuart
  2. Andrey
    Admin
    Andrey avatar
    1681 posts

    Posted 01 Aug 2011 Link to this post

    Hello Stuart,

    I think you can use the DynamicLayer.ItemContainerGenerator.StatusChanged event. When the status is ContainersGenerated then the containers are already generated for polygons. You can attach the Unloaded event for containers to remove pin points when the polygon's container is removed from the layer. Also you can use the Tag property to reference pin points and polygons.
    The sample code is below.
    private class DynamicSource : IMapDynamicSource
    {
        private MainWindow page;
        private bool updateInvoked;
     
        public DynamicSource(MainWindow examplePage)
        {
            this.page = examplePage;
            this.page.dynamicLayer.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);
        }
     
        private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            if (this.page.dynamicLayer.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated
                && !this.updateInvoked)
            {
                this.updateInvoked = true;
                this.page.dynamicLayer.Dispatcher.BeginInvoke(new Action(this.UpdatePinPoints));
            }
        }
     
        private void UpdatePinPoints()
        {
            this.updateInvoked = false;
     
            foreach (MapShape item in this.page.dynamicLayer.Items)
            {
                if (item.Tag == null)
                {
                    var containerObject = this.page.dynamicLayer.ItemContainerGenerator.ContainerFromItem(item);
                    var container = containerObject as FrameworkElement;
                    if (container != null)
                    {
                        MapPinPoint point = new MapPinPoint()
                        {
                            Text = "test",
                            Foreground = new SolidColorBrush(Colors.White)
                        };
     
                        MapLayer.SetLocation(point, item.GeographicalBounds.Center);
                        MapLayer.SetHotSpot(point, new HotSpot()
                        {
                            X = 0.5,
                            Y = 0.5
                        });
                        this.page.informationLayer.Items.Add(point);
     
                        item.Tag = point;
     
                        container.Unloaded += new RoutedEventHandler(element_Unloaded);
                        container.Tag = item;
                    }
                }
            }
        }
     
        private void element_Unloaded(object sender, RoutedEventArgs e)
        {
            // container
            var container = sender as FrameworkElement;
            if (container != null)
            {
                // polygon
                var item = container.Tag as FrameworkElement;
                container.Tag = null;
                if (item != null)
                {
                    // pin point
                    var point = item.Tag as FrameworkElement;
                    item.Tag = null;
                    if (point != null)
                    {
                        this.page.informationLayer.Items.Remove(point);
                    }
                }
            }
        }
    // ...
    }

    All the best,
    Andrey Murzov
    the Telerik team

    Register for the Q2 2011 What's New Webinar Week. Mark your calendar for the week starting July 18th and book your seat for a walk through of all the exciting stuff we will ship with the new release!

  3. UI for WPF is Visual Studio 2017 Ready
  4. Stuart
    Stuart avatar
    8 posts
    Member since:
    May 2011

    Posted 01 Aug 2011 Link to this post

    Hi Andrey,

    Thanks for your very detailed response, it certainly feels like I'm a lot closer to a solution. I have followed your sample and hooked into the ItemContainerGenerator.StatusChanged event and this works beautifully, however I still have a bit of an issue because as you'll see in the code below I'm trying to use the dynamic layer's GetItemsInLocation method to determine the shape in which the pinPoint is located. Unfortunately even though the dynamic layer's Items collection is populated, this method returns nothing, whereas is I run the code at a later point (say on map pan) then it works correctly.

     

    void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
    {
        if (uxShapeFileLayer.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated && !_updateInvoked)
        {
            _updateInvoked = true;
            uxShapeFileLayer.Dispatcher.BeginInvoke(new Action(UpdatePinPointFontColours));
        }
    }
    private void UpdatePinPointFontColours()
    {
        _updateInvoked = false;
        if (uxShapeFileLayer.Items.Count > 0)
        {
            foreach (MapPinPoint pinPoint in uxInformationLayer.Items)
            {
                if (!((bool)pinPoint.ExtendedData.GetValue("FontColourUpdated")))
                {
                    Location pinPointLocation = MapLayer.GetLocation(pinPoint);
                    List<object> shapes = uxShapeFileLayer.GetItemsInLocation(pinPointLocation).ToList();
                    foreach (object shape in shapes)
                    {
                        if (shape is MapPolygon || shape is MapPolyline)
                        {
                            Color inverseStratColour = InvertColour((((MapShape)shape).ShapeFill.Fill as SolidColorBrush).Color);
                            pinPoint.Foreground = new SolidColorBrush(inverseStratColour);
                            pinPoint.ExtendedData.SetValue("FontColourUpdated", true);
                            break;
                        }
                    }
                }
            }
        }
    }

    Any ideas?

     

    Many Thanks,
    Stuart

     

  5. Andrey
    Admin
    Andrey avatar
    1681 posts

    Posted 04 Aug 2011 Link to this post

    Hello Stuart,

    I think you can use the Loaded event of the shape container. It occurs when the polygon is visible and the GetItemsInLocation could be performed. The sample code is below.
    public partial class MainWindow : Window
    {
        private bool _attachEventInvoked;
        private bool _updateInvoked;
     
        void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            if (uxShapeFileLayer.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated && !_attachEventInvoked)
            {
                _attachEventInvoked = true;
                uxShapeFileLayer.Dispatcher.BeginInvoke(new Action(AttachEventToContainers));
            }
        }
     
        private void AttachEventToContainers()
        {
            _attachEventInvoked = false;
            foreach (MapShape item in uxShapeFileLayer.Items)
            {
                if (item.Tag == null)
                {
                    var containerObject = this.uxShapeFileLayer.ItemContainerGenerator.ContainerFromItem(item);
                    var container = containerObject as FrameworkElement;
                    if (container != null)
                    {
                        item.Tag = true;
                        container.Loaded += new RoutedEventHandler(container_Loaded);
                    }
                }
            }
        }
     
        private void container_Loaded(object sender, RoutedEventArgs e)
        {
            if (!_updateInvoked)
            {
                _updateInvoked = true;
                uxShapeFileLayer.Dispatcher.BeginInvoke(new Action(UpdatePinPointFontColours));
            }
        }
     
        private void UpdatePinPointFontColours()
        {
            _updateInvoked = false;
            if (uxShapeFileLayer.Items.Count > 0)
            {
                foreach (MapPinPoint pinPoint in uxInformationLayer.Items)
                {
                    if (!((bool)pinPoint.ExtendedData.GetValue("FontColourUpdated")))
                    {
                        Location pinPointLocation = MapLayer.GetLocation(pinPoint);
                        List<object> shapes = uxShapeFileLayer.GetItemsInLocation(pinPointLocation).ToList();
                        foreach (object shape in shapes)
                        {
                            if (shape is MapPolygon || shape is MapPolyline)
                            {
                                Color inverseStratColour = InvertColour((((MapShape)shape).ShapeFill.Fill as SolidColorBrush).Color);
                                pinPoint.Foreground = new SolidColorBrush(inverseStratColour);
                                pinPoint.ExtendedData.SetValue("FontColourUpdated", true);
                                break;
                            }
                        }
                    }
                }
            }
        }
    }

    Regards,
    Andrey Murzov
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

  6. Stuart
    Stuart avatar
    8 posts
    Member since:
    May 2011

    Posted 05 Aug 2011 Link to this post

    Hi Andrey,

    Thanks again for the great sample code and for getting back to me so quickly. Unfortunately the GetItemsInLocation method still doesn't work :o( I guess I might need to implement my own algorithm, unless you have any other ideas?

    Thanks,
    Stuart
  7. Andrey
    Admin
    Andrey avatar
    1681 posts

    Posted 10 Aug 2011 Link to this post

    Hi Stuart,

    I have another idea how to do it without using the Loaded event and containers. You can get the geometry of the polygon and use the Geometry.FillContains() method to check the points inside the polygon.
    The sample code is below:
    public partial class MainWindow : Window
    {
        private bool _updateInvoked;
        private bool initialized;
     
        private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            if (uxShapeFileLayer.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated && !_updateInvoked)
            {
                _updateInvoked = true;
                uxShapeFileLayer.Dispatcher.BeginInvoke(new Action(UpdatePinPointFontColours));
            }
        }
     
        private void UpdatePinPointFontColours()
        {
            _updateInvoked = false;
            var polygonsQuery = from MapPolygon shape
                                    in uxShapeFileLayer.Items
                                where shape.Tag == null
                                select shape;
     
            foreach (MapPolygon item in polygonsQuery)
            {
                item.Tag = true;
                var polygon = item as MapPolygon;
                var geometry = this.GetGeometry(polygon);
     
                var pointsQuery = from MapPinPoint pinPoint
                                      in uxInformationLayer.Items
                                  where !(bool)pinPoint.ExtendedData.GetValue("FontColourUpdated")
                                  && geometry.FillContains((Point)MapLayer.GetLocation(pinPoint))
                                  select pinPoint;
     
                foreach (MapPinPoint pinPoint in pointsQuery)
                {
                    Color inverseStratColour = InvertColour((polygon.ShapeFill.Fill as SolidColorBrush).Color);
                    pinPoint.Foreground = new SolidColorBrush(inverseStratColour);
                    pinPoint.ExtendedData.SetValue("FontColourUpdated", true);
                    break;
                }
            }
        }
     
        private Geometry GetGeometry(MapPolygon mapPolygon)
        {
            PathGeometry pathGeo = new PathGeometry();
            PathFigure pathFigure = new PathFigure();
            PolyLineSegment seg = new PolyLineSegment();
            PointCollection ponts = new PointCollection(mapPolygon.Points.Count);
            foreach (Point point in mapPolygon.Points)
            {
                ponts.Add(point);
            }
            seg.Points = ponts;
     
            pathFigure.StartPoint = seg.Points[0];
            pathFigure.Segments.Add(seg);
            pathGeo.Figures.Add(pathFigure);
     
            return pathGeo;
        }
    }

    Greetings,
    Andrey Murzov
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

Back to Top
UI for WPF is Visual Studio 2017 Ready