Bing maps data bound pushpins fade-in animation

When you use Bing Maps and place pushpins on it, you have two basic options. One is to fetch all the pushpins (actually, locations) from some service and then programmatically add them to the map, or you can bind all the pins (locations) through MapItemsControl with ObservableCollection. In this article, I’ll show you how to add pins from the background worker in case you’re fetching a lot of them at the same time and you’re using data binding, and also how to create a smooth animation to make the pins fade-in as they become available. Smile

I will continue with one of my last articles where I explained how to create a sort of a tooltip for pushpins, when you tap on them. In case you missed that one, here it is:

https://igrali.wordpress.com/2012/01/07/show-a-tooltip-for-tapped-pushpin-on-windows-phone/

If you would like the whole project, find it here:

https://skydrive.live.com/redir.aspx?cid=c8b1f62c5b2dc515&resid=C8B1F62C5B2DC515!260&parid=C8B1F62C5B2DC515!259&authkey=!AA0equ7I9MM6bi0

Now, instead of simply adding all the pins to the map, I’ll simulate a long background worker that gets them from a web service by simply putting the thread to sleep for 2000 milliseconds. Unfortunately, we can add the items to ObservableCollection in a thread safe manner so it’s necessary that we either use a simple List and then copy all the items to ObservableCollection (which would defeat the purpose of it), or we can report progress from the BackgroundWorker and then simply add the pushpins from there. Here’s what I mean. We change the PushpinViewModel.cs from the last article to look like this:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Device.Location;
using PushpinTooltipSample.Models;


namespace PushpinTooltipSample.ViewModels
{
    public class PushpinViewModel
    {
      private ObservableCollection<PushpinModel> _pushpins = 
                                   new ObservableCollection<PushpinModel>();

      private BackgroundWorker worker;

      public ObservableCollection<PushpinModel> Pushpins
        {
            get
            {
                return _pushpins;
            }
        }

      public PushpinViewModel()
        {
            worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.ProgressChanged += 
                new ProgressChangedEventHandler(worker_ProgressChanged);
            worker.WorkerReportsProgress = true;
            worker.RunWorkerAsync();
        }

      void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            PushpinModel ppModel = e.UserState as PushpinModel;
            _pushpins.Add(ppModel);
        }

      void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            var pp1 = new PushpinModel
            {
                Description = "Popular tourist destination in Croatia",
                Name = "Dubrovnik",
                Location = new GeoCoordinate(42.642749786377,
                                                18.1106491088867),
                DatetimeAdded = DateTime.Now
            };

            System.Threading.Thread.Sleep(2000);
            worker.ReportProgress(25, pp1);

            var pp2 = new PushpinModel
            {
                Description = "Diocletian's Palace is in this city",
                Name = "Split",
                Location = new GeoCoordinate(43.5069808959961,
                                                16.4417095184326),
                DatetimeAdded = DateTime.Now.AddMonths(-10)
            };

            System.Threading.Thread.Sleep(2000);
            worker.ReportProgress(50, pp2);

            var pp3 = new PushpinModel
            {
                Description = "Capital of Croatia",
                Name = "Zagreb",
                Location = new GeoCoordinate(45.807258605957,
                                                15.9675998687744),
                DatetimeAdded = DateTime.Now.AddYears(-1)
            };

            System.Threading.Thread.Sleep(2000);
            worker.ReportProgress(75, pp3);

            var pp4 = new PushpinModel
            {
                Description = "Popular for Tvrda, Old Town of the city",
                Name = "Osijek",
                Location = new GeoCoordinate(45.5584487915039,
                                                18.6764907836914),
                DatetimeAdded = DateTime.Now.AddDays(-10)
            };

            System.Threading.Thread.Sleep(2000);
            worker.ReportProgress(100, pp4);
        }
    }
}

The simulation is rather simple. We use the BackgroundWorker like we might in case we used a real service here, and every time we got a new pushpin, we reported progress to get back to the main thread and add the pushpin to the ObservableCollection. We simply sent the pushpin as the parameter of the ReportProgress method, casted it in the ProgressChanged event handler and added to the ObservableCollection. If you tested the code now, you would notice that it displays a new pin every 2 seconds, but it does it ugly with no animation. Let’s add it ourselves!

Add fade-in animation to Pushpins

First of all, change the MainPage.xaml.cs file to look like this:

using System.Linq;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Controls.Maps;
using PushpinTooltipSample.ViewModels;
using System.Device.Location;

namespace PushpinTooltipSample
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructor

        PushpinViewModel _viewModel;

        public MainPage()
        {
            _viewModel = new PushpinViewModel();
            InitializeComponent();
        }

        private void Map_Loaded(object sender, RoutedEventArgs e)
        {
            mapPins.ItemsSource = _viewModel.Pushpins;
            Mapa.Center = new GeoCoordinate(45.807258605957,
                                                15.9675998687744);
            Mapa.ZoomLevel = 6;
        }

        private void Pushpin_Tap(object sender, 
            System.Windows.Input.GestureEventArgs e)
        {
            var _ppmodel = sender as Pushpin;
            ContextMenu contextMenu = 
                ContextMenuService.GetContextMenu(_ppmodel);
            contextMenu.DataContext = _viewModel.Pushpins.Where
                (c => (c.Location 
                    == _ppmodel.Location)).FirstOrDefault();
            if (contextMenu.Parent == null)
            {
                contextMenu.IsOpen = true;
            }
        }
    }
}

The difference is that we now zoomed in to Croatia to make sure the pushpins are clear and visible immediately after running the app. In the MainPage.xaml, inside the MapItemsControl pushpin datatemplate, add the following animations that changes the Opacity from default 0 to 1 triggered by the pushpin getting loaded:

<my:Pushpin.Triggers>
    <EventTrigger RoutedEvent="Canvas.Loaded">
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimation Duration="0:0:1" To="1" 
                                 Storyboard.TargetProperty="Canvas.Opacity" 
                                 Storyboard.TargetName="PointMe" />
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</my:Pushpin.Triggers>

So the whole MapItemsControl datatemplate now looks like this:

<DataTemplate>
<my:Pushpin
    Background="Blue"
    Location="{Binding Location}" Tap="Pushpin_Tap" Name="PointMe" 
    Opacity="0">
<my:Pushpin.Triggers>
    <EventTrigger RoutedEvent="Canvas.Loaded">
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimation Duration="0:0:1" To="1" 
                                 Storyboard.TargetProperty="Canvas.Opacity" 
                                 Storyboard.TargetName="PointMe" />
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</my:Pushpin.Triggers>
       <sltkit:ContextMenuService.ContextMenu>
        <sltkit:ContextMenu IsZoomEnabled="False" 
                Style="{StaticResource MenuStyle}">
            <sltkit:MenuItem 
                Style="{StaticResource MenuItemStyle}"/>
        </sltkit:ContextMenu>
    </sltkit:ContextMenuService.ContextMenu>
</my:Pushpin>
</DataTemplate>

Now when you run the app, every pin that’s added to the ObservableCollection binded to the Map items source now fades-in slowly to make it more natural.

I hope this helps you make a better data bound map in your app.

fadein

Questions? Twitter! Or leave a comment Winking smile

11 thoughts on “Bing maps data bound pushpins fade-in animation

  1. Hi, sorry but I still have a problem with the application resource. The error is: – Type “sltkit:MenuItem” was not found – . More info of my problem (for me Im not working in the MainPage.xaml, im using a new Portrait Page called Map.xaml). Do i have to create a reference or something like that because of this?. Because in the Map.xaml on te and in the MenuStyle too it says that the resource can be resolved.
    Thank you,

    • Bing Maps are in the Microsoft.Phone.Controls.Maps namespace, and to be able to use the ContextMenu you need to install Silverlight for Windows Phone toolkit available on CodePlex.

  2. Thanks i was missing the “sltkit” reference in te App.xaml.
    But now I want to retrive the information inside the contextMenu (Name, Location, DateTime -and I create an “Id” too) because i want to use it in the MainPage.xaml.cs, for example i want to assign the Name, Location and DateTime to local variables in the code behind MainPage.xaml.cs. What I want to do is when I tap the contexMenu (of one pushpin) I want to open another PortraitPage and show more details of the current tapped pushpin. How can I do this?? I’ve been trying but I was unable too :(
    thanks again.

    • If you can get the ID of the item from the ContextMenu, you can pass that ID as a parameter to the PortraitPage

      NavigationService.Navigate(new Uri(“/PortraitPage.xaml?id=”+someID), UriKind.Relative);

      and then when you get to the PortraitPage, get the ID and then get more details for that ID and show it.

  3. Thanks again (sorry for asking so much)

    Yes, thats what i’m trying to do but i don’t know how to access to the ID, Name, Location or DateTime in the code behind or xaml when I Tap the ContextMenu (of the selected PushPin).
    In the xaml I add a HyperLinkButton an in the “NavigateUri” i try to add the {Binding ID} at the end, like this:

    …but always throw me an exception, i don’t know the corect syntax for doing this.

    And also try in the code behind, in the Tap event of the contextMenu in the MainPage.xaml but I couldn’t access to the ID, Name, Location or DateTime, I try “ContextMenu.Items….” ContextMenu.GetValues….” I don’t know the syntax or where the values of the ID, Name, Location or DateTime is allocated or stored to retrieve it and then send it to the DetailsPage.xaml.

    thanks

  4. This is the “NavigationUri” that I try use (i don’t know why it doesn’t show up in the last comment)
    HyperlinkButton Content=”{Binding Name}” NavigateUri=”/DetailsPage.xaml?{Binding ID}”/>
    …as I say, i don’t know the corect syntax for doing this

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s