Posts Tagged Bing Maps

Creating Hover-Style Info Boxes on the Bing Maps AJAX v7.0 Control

When v7.0 of the Bings Maps AJAX control was released last fall, I began testing out how easy it would be to port some of my existing code that was originally developed against the v6.3 AJAX control. I was pleasantly surprised to find the newer version performed considerably faster, and had a more natural API. However, many of the features that v6.3 gives you OTB were not available in v7.0; info boxes being one of those features. Thus I began testing how easy it would be to develop my own info boxes. I got about 90% of the way there before running into some snags with the way mouse events and pushpins interacted that made building display-on-hover style info boxes problematic. You can read more about this experience on Windows Live Developer Forums – Creating Infoboxes in Bing Maps AJAX v7.

Microsoft recently released an update for the Bing Maps AJAX v7.0 control which includes the ability to create info boxes. The initial release did not offer this feature so this is a welcome improvement. It also seems to have addressed the mouse event issues with push-pins. Using information published in the MSDN documentation and the experiences gathered from other developer’s forum postings, I have put together an example of how to use the new info box features and create hover-style info boxes that allow clickable content. Unlike the examples provided in the API documentation, my solution doesn’t require that the user click the pin to display the info box, or click the close button to hide it . Instead, they will show by hovering the mouse over the pushpin, will stay visible as long as the mouse remains on the pushpin or info box, and will then automatically hide as the mouse moves off the info box or pushpin.

Sample code showing how this can be done is provided below.

Code Snippet
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;>
<html xmlns="http://www.w3.org/1999/xhtml&quot;>
<head>
    <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&quot;></script>
    <script type="text/javascript">
        var map = null;
        var pinInfobox = null;

        // create a map object and place two test pins on it, with infobox on pin hover.
        function GetMap() {
            // Initialize the map
            var mapSettings = {
                // MapOptions
                credentials: "BING MAP CODE GOES HERE",
                // ViewOptions
                // default to roughly center of CO.
                center: new Microsoft.Maps.Location(39.1000, -105.6500),
                // this gives a combo arial and birdseye in v7
                mapTypeId: Microsoft.Maps.MapTypeId.birdseye,
                padding: 1,
                zoom: 7 // shows the whole state of CO
            };
            map = new Microsoft.Maps.Map(document.getElementById("myMap"), mapSettings);
            // Hide the info box when the map is moved.
            Microsoft.Maps.Events.addHandler(map, 'viewchange', mapViewChange);

            // Retrieve the location of the map center
            var pinLocation = map.getCenter();
            // Add a pin to the center of the map
            var pin = new Microsoft.Maps.Pushpin(pinLocation, { text: '1' });
            //Microsoft.Maps.Events.addHandler(pin, 'click', displayInfobox);
            Microsoft.Maps.Events.addHandler(pin, 'mouseover', pinMouseOver);
            Microsoft.Maps.Events.addHandler(pin, 'mouseout', pinMouseOut);
            // Add the pushpin to the map
            map.entities.push(pin);

            // create a second pin
            pinLocation = new Microsoft.Maps.Location(39.0000, -105.6000)
            pin = new Microsoft.Maps.Pushpin(pinLocation, { text: '2' });
            Microsoft.Maps.Events.addHandler(pin, 'mouseover', pinMouseOver);
            Microsoft.Maps.Events.addHandler(pin, 'mouseout', pinMouseOut);
            map.entities.push(pin);

        }

        // This function will create an infobox
        // and then display it for the pin that triggered the hover-event.
        function displayInfobox(e) {
            // make sure we clear any infoBox timer that may still be active
            stopInfoboxTimer(e);

            // build or display the infoBox
            var pin = e.target;
            if (pin != null) {

                // Create the info box for the pushpin
                var location = pin.getLocation();
                var options = {
                    id: 'infoBox1',
                    title: 'My Pushpin Title',
                    description: 'This is the plain text description.',
                    //htmlContent: '',
                    height: 100,
                    width: 150,
                    visible: true,
                    showPointer: true,
                    showCloseButton: true,
                    // offset the infobox enough to keep it from overlapping the pin.
                    offset: new Microsoft.Maps.Point(0, pin.getHeight()),  
                    zIndex: 999
                };
                // destroy the existing infobox, if any
                // In testing, I discovered not doing this results in the mouseleave
                // and mouseenter events not working after hiding and then reshowing the infobox.
                if (pinInfobox != null) {
                    map.entities.remove(pinInfobox);
                    if (Microsoft.Maps.Events.hasHandler(pinInfobox, 'mouseleave'))
                        Microsoft.Maps.Events.removeHandler(pinInfobox.mouseLeaveHandler);
                    if (Microsoft.Maps.Events.hasHandler(pinInfobox, 'mouseenter'))
                        Microsoft.Maps.Events.removeHandler(pinInfobox.mouseEnterHandler);
                    pinInfobox = null;
                }
                // create the infobox
                pinInfobox = new Microsoft.Maps.Infobox(location, options);
                // hide infobox on mouseleave
                pinInfobox.mouseLeaveHandler
                    = Microsoft.Maps.Events.addHandler(pinInfobox, 'mouseleave', pinInfoboxMouseLeave);
                // stop the infobox hide timer on mouseenter
                pinInfobox.mouseEnterHandler
                    = Microsoft.Maps.Events.addHandler(pinInfobox, 'mouseenter', pinInfoboxMouseEnter);
                // add it to the map.
                map.entities.push(pinInfobox);
            }
        }

        function hideInfobox(e) {
            if (pinInfobox != null)
                pinInfobox.setOptions({ visible: false });
        }

        // This function starts a count-down timer that will hide the infoBox when it fires.
        // This gives the user time to move the mouse over the infoBox, which disables the timer
        // before it can fire, thus allowing clickable content in the infobox.
        function startInfoboxTimer(e) {
            // start a count-down timer to hide the popup.
            // This gives the user time to mouse-over the popup to keep it open for clickable-content.
            if (pinInfobox.pinTimer != null) {
                clearTimeout(pinInfobox.pinTimer);
            }
            // give 300ms to get over the popup or it will disappear
            pinInfobox.pinTimer = setTimeout(timerTriggered, 300);
        }

        // Clear the infoBox timer, if set, to keep it from firing.
        function stopInfoboxTimer(e) {
            if (pinInfobox != null && pinInfobox.pinTimer != null) {
                clearTimeout(pinInfobox.pinTimer);
            }
        }

        function mapViewChange(e) {
            stopInfoboxTimer(e);
            hideInfobox(e);
        }
        function pinMouseOver(e) {
            displayInfobox(e);
        }
        function pinMouseOut(e) {
            // TODO: detect if the mouse is already over the infoBox
            //  This can happen when the infobox is shown overlapping the pin where the mouse is at
            //    In that case, we shouldn't start the timer.
            startInfoboxTimer(e);
        }
        function pinInfoboxMouseLeave(e) {
            hideInfobox(e);
        }
        function pinInfoboxMouseEnter(e) {
            // NOTE: This won't fire if showing infoBox ends up putting it under the current mouse pointer.
            stopInfoboxTimer(e);
        }
        function timerTriggered(e) {
            hideInfobox(e);
        }
    </script>
</head>
<body onload="GetMap();">
    <div id='myMap' style="position: relative; width: 500px; height: 500px;">
    </div>
</body>
</html>

, ,

18 Comments

%d bloggers like this: