This is a migrated thread and some comments may be shown as answers.

Inverse Font Colour on MapPinPoint

5 Answers 79 Views
Map
This is a migrated thread and some comments may be shown as answers.
Stuart
Top achievements
Rank 1
Stuart asked on 28 Jul 2011, 07:36 AM
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

5 Answers, 1 is accepted

Sort by
0
Andrey
Telerik team
answered on 01 Aug 2011, 08:09 AM
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!

0
Stuart
Top achievements
Rank 1
answered on 02 Aug 2011, 01:06 AM
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

 

0
Andrey
Telerik team
answered on 04 Aug 2011, 09:15 AM
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 >>

0
Stuart
Top achievements
Rank 1
answered on 05 Aug 2011, 06:53 AM
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
0
Andrey
Telerik team
answered on 10 Aug 2011, 03:12 PM
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 >>

Tags
Map
Asked by
Stuart
Top achievements
Rank 1
Answers by
Andrey
Telerik team
Stuart
Top achievements
Rank 1
Share this question
or