Bing Maps WPF Internet Connection Issue

The Bing Maps WPF control is a fairly new map control which had it’s first version released in January of this year. As with anything that’s new there is bound to be a couple issues that need working out. On issue that has been mentioned on the Bing Maps forums a number of time has to do with the map control crashing if you load it up with out an internet connection. The exact steps to reproduce this issue are:

  1. Disconnect from the internet and start your application. Ensure that your map defaults to either the Road or Aerial map modes. The map control will load and you will likely see an empty map, and possibly some cached tiles. You can pan and zoom the map but can only really tell by the scale bar which changes when you zoom.
  2. Re-connect to the internet and try to pan or zoom the map. The application will crash.

Trying to catch this exception is not something that can be easily done. The Bing Maps WPF control is designed to work with an internet connection as it needs this to authenticate the map session for the map control.

So how do we prevent our application from crashing and handle this issue in a nicer way. Simple, monitor the internet connection and don’t load the map until there is an internet connection. Since this is a matter of creating a better user experience we might as well also create a useful error message an toss in a screen shot of a map in background.

The first thing we will want to do is create a container for the map control. This can be easily done by creating a new class called MapContainer and having it inherit from the Grid class. We can add a simple event to this class which gets fired when the map is loaded to the container. This will allow us to tie the map control back into our application so we can add our credentials and set the map view. In the constructor of this MapContainer class we can attach to the Loaded event of the Grid. When the Grid is loaded we can check if there is an internet connection. If there is we can then create a map instance, add it to the grid and call the MapLoaded event. If there isn’t an internet connection we can then add a NetworkAvailibilityChanged event handler which will monitor the internet connection for us and will load the map control as soon as an internet connection is available. We can add in an error message and a map screen shot into the grid while there is no internet connection so that the user is aware of why the map is not available. If an internet connection does become available we can then empty the grid, create a new map instance, and add it to the grid and call the MapLoaded event. Here is the code for the MapContainer class (MapContainer.cs):

using System;
using System.Net.NetworkInformation;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Microsoft.Maps.MapControl.WPF;

namespace BMWPFInternetBug
{
    public class MapContainer : Grid
    {
        public Action<Map> MapLoaded;

        #region Private Properties

        private Map _map;
        private bool _mapLoaded;

        #endregion

        #region Constructor

        public MapContainer()
        {
            this.Loaded += (s, e) =>
            {
                if (NetworkInterface.GetIsNetworkAvailable())
                {
                    _map = new Map();
                    this.Children.Add(_map);

                    _mapLoaded = true;

                    if (MapLoaded != null)
                    {
                        MapLoaded(_map);
                    }
                }
                else
                {
                    NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);

                    this.Children.Add(new Grid()
                    {
                        Background = new SolidColorBrush(Colors.Transparent),
                        Children =
                        {
                            //Optional Background image of a map.
                            new Image()
                            {
                                Source = new BitmapImage(new Uri("pack://application:,,,/BackgroundMap.png", UriKind.RelativeOrAbsolute))
                            },

                            //Error Message due to lack of internet connection
                            new Border(){
                                Background = new SolidColorBrush(Colors.White),
                                CornerRadius = new System.Windows.CornerRadius(10),
                                Padding = new Thickness(10),
                                HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
                                VerticalAlignment = System.Windows.VerticalAlignment.Center,
                                Child = new TextBlock()
                                {
                                    Text = "Unable to load map without Internet Connection.",
                                    HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
                                    VerticalAlignment = System.Windows.VerticalAlignment.Center
                                }
                            }
                        }
                    });
                }
            };
        }

        #endregion

        #region Public Methods

        public Map Map
        {
            get { return _map; }
        }

        public bool IsMapLoaded
        {
            get { return _mapLoaded; }
        }

        #endregion

        #region Private Methods

        private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs args)
        {
            if (args.IsAvailable)
            {
                this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
                {
                    this.Children.Clear();

                    _map = new Map();
                    this.Children.Add(_map);

                    _mapLoaded = true;

                    if (MapLoaded != null)
                    {
                        MapLoaded(_map);
                    }

                    NetworkChange.NetworkAvailabilityChanged -= NetworkChange_NetworkAvailabilityChanged;
                }));
            }
        }

        #endregion
    }
}

Here is the screen shot of the map I’m using for the background image. Right click and save the image as BackgroundImage.png. Note that the build action for this image should be set to Resource. BackgroundMap

Your file structure should look something like this:

image

Next you can add an instance of the MapContainer class to your XAML. Here is a basic example of a UI that has a map and two buttons for changing the map mode:

<Window x:Class="BMWPFInternetBug.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:m="clr-namespace:BMWPFInternetBug"
        Width="800" Height="600">
    <Grid>
        <m:MapContainer x:Name="MyMapContainer"/>

        <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
            <Button Content="Road" Click="RoadBtn_Clicked" Height="25" Width="50"/>
            <Button Content="Aerial" Click="AerialBtn_Clicked" Height="25" Width="50"/>
        </StackPanel>
    </Grid>
</Window>

Next we will want to tie into the MapLoaded event of the MapContainer in the constructor of the Window and add code to set the credentials to the map and any additional load events you want to add.

public MainWindow()
{
    InitializeComponent();

    MyMapContainer.MapLoaded += (map) =>
    {
        map.CredentialsProvider = new ApplicationIdCredentialsProvider("YOUR_BING_MAPS_KEY");

        //Add additional post map load functionalities as needed. i.e. set map view
    };
}

Finally we can add in the button event handlers for our map mode selector buttons. Note that since the map may not be loaded when the application is loaded we should check to see if it is loaded first.

private void RoadBtn_Clicked(object sender, RoutedEventArgs e)
{
    if (MyMapContainer.IsMapLoaded)
    {
        MyMapContainer.Map.Mode = new RoadMode();
    }
}

private void AerialBtn_Clicked(object sender, RoutedEventArgs e)
{
    if (MyMapContainer.IsMapLoaded)
    {
        MyMapContainer.Map.Mode = new AerialMode();
    }
}

Now if you run this application without an internet connection you should see something like the image below. If you connect to the internet without restarting your application you should see it load up your map. This makes solution ideal for situations where the internet connection may not be reliable. Note, if you lose an internet connection after the map is loaded the map will continue to work but no new tiles will be loaded in.

image

6 thoughts on “Bing Maps WPF Internet Connection Issue

  1. Hi Richard,

    thanks for presenting this nice idea for a workaround.

    But this workaround will only work in very specific cases. It might work when you pull out the network cable so the network becomes generally unavailable. But it will not work, if the network (LAN) is permanently available, but only the Internet connection is temporarily out-of-order (e. g. because in a corporate network the proxy settings are wrong).

    In my case I’m using the Bing Maps control more or less as a container panel with georeferencing capabilities drawing some custom layers above it and with support for a MapTileLayer showing OSM (OpenStreetMap) tiles from an offline(!) cache. The user is able to switch between Bing Map tiles when he is online, and/or OSM tiles when he is offline.

    So as long as the control is in MercatorMode, I don’t have a problem with the missing Internet connection. It’s still a very helpful control, even with no Internet connection.

    Are there any plans for a bugfix release? The Bing Maps WPF control is really great for presenting georeferenced data with and even without using the Bing Map tiles. The design, performance and user experience are phenomenal. There are only some minor flaws (like this one here) that require fixing.

    Best regards,
    Chris

    • The use case you are discribing is against the terms of use of Bing Maps. Using the Bing Maps controls offline is against the terms of use, even if you are using tiles other than Bing Map tiles. Open Street Map tile integration is currently against the terms of use as well. As for a bug fix, I can’t speak for the development team but I would hope this gets fixed, or at least is managed in a better way.

    • For those asking for the source code, my company policy prevents me from making source code available through my blog. Note that this technical blog is extremely simple to put together.

  2. Pingback: Bing map control crash with null reference exception | Technology & Programming

Leave a comment