Using the InfoWindow to create MapTips

Users often expect to have an interactive experience with the map that includes getting additional information about elements by tapping and holding them. By adding MapTips to your ArcGIS API for Windows Phone applications, you can display information about graphic features on the tap and hold gesture. With the ArcGIS API for Windows Phone, you can easily data-bind MapTips so that displayed information is always based on the attributes of the current graphic. To add MapTips to an application that already contains one or more graphics layers, do the following:

The InfoWindow is a control available as part of the ESRI.ArcGIS.Client.Toolkit assembly, designed to give an easy "pop-up" experience in Windows Phone applications. Another common user experience is to have additional information available off of a MapTip. Another control, the ChildPage, available in the ESRI.ArcGIS.Client.Toolkit.Primitives assembly, helps create that experience. This topic builds an example application with MapTips and provides some tips for working with an InfoWindow. In the Interactive SDK, see the Toolkit controls samples for some complete samples using the InfoWindow.

Building an application with MapTips

This section details the steps for building an application with MapTips. It includes creating an additional information page accessible from the MapTip, as well as handling when the user clicks both single and multiple features. The following steps are used to create the application:

  1. Adding an InfoWindow showing MapTips
  2. Adding a details page
  3. Handling multiple selected graphics

Adding an InfoWindow showing MapTips

An example application with InfoWindow MapTips on a FeatureLayer (which is a type of graphics layer) is created in the following steps. The application displays states with a population density of more than 150. For each state, a MapTip showing the state's name and population density displays.

NoteNote:

MapTips can be created for a GraphicsLayer in the same way. A FeatureLayer is used for illustration because it can be populated with feature graphics without the use of managed .NET code.

  1. Set up your application to include a Map control containing a basemap layer, a FeatureLayer, and a symbol for the FeatureLayer. The FeatureLayer should only display features with a population density over 150 and use the MySimpleRenderer to apply the MyFillSymbol resource. The following code section shows the Grid.Resources XAML to create the symbol MyFillSymbol and the renderer MySimpleRenderer in the LayoutRoot element.
    <Grid.Resources>
        <esriSymbols:SimpleFillSymbol x:Key="MyFillSymbol" 
            Fill="#66FF0000" BorderBrush="Red" BorderThickness="2" />
        <esri:SimpleRenderer x:Key="MySimpleRenderer" Symbol="{StaticResource MyFillSymbol}" />
    </Grid.Resources>
    
    The following code shows the ContentPanel element's XAML:
    <esri:Map x:Name="MyMap" Extent="-130, 10, -70, 60" >
        <esri:Map.Layers>
            <esri:ArcGISTiledMapServiceLayer ID="StreetMapLayer"
                Url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer">
            </esri:ArcGISTiledMapServiceLayer>
            <esri:FeatureLayer ID="MyFeatureLayer" 
                Url="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5"
                Where="POP07_SQMI > 150" 
                Renderer="{StaticResource MySimpleRenderer}" >
                <esri:FeatureLayer.OutFields>
                    <sys:String>STATE_NAME</sys:String>
                    <sys:String>POP07_SQMI</sys:String>
                </esri:FeatureLayer.OutFields>
            </esri:FeatureLayer>
        </esri:Map.Layers>
    </esri:Map>
    
    For additional details, see Creating a map, Feature layers, and Symbols and renderers.
    NoteNote:
    • Don't forget to add references to the ESRI.ArcGIS.Client and System.Runtime.Serialization assemblies to your project.
    • Make sure you include XML namespace references for the ESRI.ArcGIS.Client and ESRI.ArcGIS.Client.Symbols namespaces in the ESRI.ArcGIS.Client assembly, as well as for the System namespace in the mscorlib assembly, as attributes on the phone:PhoneApplicationPage element.
      xmlns:esri="clr-namespace:ESRI.ArcGIS.Client;assembly=ESRI.ArcGIS.Client"
      xmlns:esriSymbols="clr-namespace:ESRI.ArcGIS.Client.Symbols;assembly=ESRI.ArcGIS.Client"
      xmlns:sys="clr-namespace:System;assembly=mscorlib"
      
  2. Add a reference to the ESRI.ArcGIS.Client.Toolkit assembly, which contains the InfoWindow.
  3. Include an XML namespace reference for the ESRI.ArcGIS.Client.Toolkit namespace as an attribute on the phone:PhoneApplicationPage element.
    xmlns:esriToolkit="clr-namespace:ESRI.ArcGIS.Client.Toolkit;assembly=ESRI.ArcGIS.Client.Toolkit"
    
  4. Following the Map in the ContentPanel element, add an InfoWindow element. The MapTip's display is defined in this element. Provide an x:Name value for the element, as well as binding it to the map it interacts with. Specify IsOpen equals False, since the InfoWindow should not be open by default. Some style properties, including the border, background, and foreground colors, are also set.
    <esriToolkit:InfoWindow x:Name="infoWinMapTip" 
        BorderBrush="Black" Background="LightYellow" Foreground="Black"
        Map="{Binding ElementName=MyMap}" IsOpen="False">
    </esriToolkit:InfoWindow>
    
  5. Inside the InfoWindow element, define the container for the MapTip's content. In the following code, a StackPanel is used. The margin is specified on the StackPanel. The InfoWindow automatically resizes to fit its contents.
    <esriToolkit:InfoWindow x:Name="infoWinMapTip" 
        BorderBrush="Black" Background="LightYellow" Foreground="Black"
        Map="{Binding ElementName=MyMap}" IsOpen="False">
        <StackPanel Margin="5">
        </StackPanel>
    </esriToolkit:InfoWindow>
    
  6. Inside the InfoWindow's StackPanel, add TextBlock to hold the state name.
    <esriToolkit:InfoWindow x:Name="infoWinMapTip" 
        BorderBrush="Black" Background="LightYellow" Foreground="Black"
        Map="{Binding ElementName=MyMap}" IsOpen="False">
        <StackPanel Margin="5">
            <TextBlock FontWeight="Bold" />
        </StackPanel>
    </esriToolkit:InfoWindow>
    
  7. To display in the MapTip the name of the state being tapped and held, you'll bind the text of the state name TextBlock to the STATE_NAME field. This field is already specified as one of the FeatureLayer's OutFields, so you know it is available for binding. The best way to bind to this field is to use the DictionaryConverter object. The DictionaryConverter allows you to make a data binding to a specific key in a Dictionary. The DictionaryConverter is defined in the ESRI.ArcGIS.Client.ValueConverters namespace of the ESRI.ArcGIS.Client assembly. To declare a DictionaryConverter in XAML, map an XML namespace to this CLR namespace. This mapping should be placed as an attribute on the phone:PhoneApplicationPage element.

    xmlns:esriConverters="clr-namespace:ESRI.ArcGIS.Client.ValueConverters;assembly=ESRI.ArcGIS.Client"
    
  8. Add a DictionaryConverter as a resource in the LayoutRoot element.
    <Grid.Resources>
        <esriSymbols:SimpleFillSymbol x:Key="MyFillSymbol" Fill="#66FF0000" BorderBrush="Red" BorderThickness="2" />
        <esri:SimpleRenderer x:Key="MySimpleRenderer" Symbol="{StaticResource MyFillSymbol}" />
        <esriConverters:DictionaryConverter x:Key="MyDictionaryConverter" />
    </Grid.Resources>
    
  9. Within the InfoWindow element, the DataContext (what data binding tries to attach to) is the graphic that has been tapped and held. Its Attributes property is a Dictionary, where each item's Key is a field name and the Value is that field's value for the current graphic. So, the DictionaryConverter allows you to easily bind to a field when declaring an InfoWindow. In the state name TextBlock, specify the Text property. Use the DictionaryConverter to bind to the STATE_NAME field as shown in the following code:
    <esriToolkit:InfoWindow x:Name="infoWinMapTip" 
        BorderBrush="Black" Background="LightYellow" Foreground="Black"
        Map="{Binding ElementName=MyMap}" IsOpen="False">
        <StackPanel Margin="5">
            <TextBlock Text="{Binding Attributes, 
                Converter={StaticResource MyDictionaryConverter},
                ConverterParameter=STATE_NAME, Mode=OneWay}" 
                FontWeight="Bold" />     
        </StackPanel>
    </esriToolkit:InfoWindow>
    
  10. Add a StackPanel to hold a label and value for the population density. Specify the orientation as Horizontal so the value appears next to the label.
    <esriToolkit:InfoWindow x:Name="infoWinMapTip" 
        BorderBrush="Black" Background="LightYellow" Foreground="Black"
        Map="{Binding ElementName=MyMap}" IsOpen="False">
        <StackPanel Margin="5">
            <TextBlock Text="{Binding Attributes, 
                Converter={StaticResource MyDictionaryConverter},
                ConverterParameter=STATE_NAME, Mode=OneWay}"
                FontWeight="Bold" />     
            <StackPanel Orientation="Horizontal">
            </StackPanel>
        </StackPanel>
    </esriToolkit:InfoWindow>
    
  11. Inside the StackPanel, add TextBlocks for the population density label and population density value. Using the DictionaryConverter, bind the text to the POP07_SQMI field.
    <esriToolkit:InfoWindow x:Name="infoWinMapTip" 
        BorderBrush="Black" Background="LightYellow" Foreground="Black"
        Map="{Binding ElementName=MyMap}" IsOpen="False">
        <StackPanel Margin="5">
            <TextBlock Text="{Binding Attributes, 
                Converter={StaticResource MyDictionaryConverter},
                ConverterParameter=STATE_NAME, Mode=OneWay}" 
                FontWeight="Bold" />     
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Population Density (2007): " />
                <TextBlock Text="{Binding Attributes,
                    Converter={StaticResource MyDictionaryConverter}, 
                    ConverterParameter=POP07_SQMI, Mode=OneWay}" />
            </StackPanel>
        </StackPanel>
    </esriToolkit:InfoWindow>
    
  12. Add a MapGesture event handler to the map.
    <esri:Map x:Name="MyMap" Extent="-130, 10, -70, 60" MapGesture="MyMap_MapGesture">
    
  13. Switch to the code-behind to implement the MyMap_MapGesture event handler. Add a using statement for the ESRI.ArcGIS.Client namespace.
    using ESRI.ArcGIS.Client;
    
  14. Check the type of gesture that was intercepted, and if it was GestureType.Hold, set IsOpen to false to close any open MapTips.
    private void MyMap_MapGesture(object sender, Map.MapGestureEventArgs e)
    {
        if (e.Gesture == GestureType.Hold)
        {
            infoWinMapTip.IsOpen = false;
        }
    }
    
  15. Check if any graphics were held. If so, center on the first of the graphics using PanTo and display its MapTip InfoWindow by setting IsOpen to true.
    private void MyMap_MapGesture(object sender, Map.MapGestureEventArgs e)
    {
        if (e.Gesture == GestureType.Hold)
        {
            infoWinMapTip.IsOpen = false;
    
            List<Graphic> results = new List<Graphic>(e.DirectlyOver(10));
            if (results.Count > 0)
            {
                MyMap.PanTo(results[0].Geometry);
    
                infoWinMapTip.DataContext = results[0];
                infoWinMapTip.Anchor = results[0].Geometry.Extent.GetCenter();
                infoWinMapTip.IsOpen = true;
            }
        }
    }
    
  16. Run your application. Tap and hold a state to see the MapTip. See the following screen shot:
    Screen shot of an InfoWindow showing some state information.

Adding a details page

Often, MapTips are just some basic information and a feature has additional information that needs to be available as well. One way to do this is to create a details page that the MapTip can open. The ESRI.ArcGIS.Client.Toolkit.Primitives contains a ChildPage element that makes a good details page user experience. A ChildPage is a sliding panel that comes in to display additional information without actually leaving the current page, and whose contents are completely up to you as the developer. Building on the MapTips code in the previous section, the following steps add a details page using a ChildPage:

  1. Update the FeatureLayer to return some additional fields that will be used on the details page.
    <esri:FeatureLayer ID="MyFeatureLayer" 
        Url="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5"
        Where="POP07_SQMI > 150" 
        Renderer="{StaticResource MySimpleRenderer}" >
        <esri:FeatureLayer.OutFields>
            <sys:String>STATE_NAME</sys:String>
            <sys:String>POP07_SQMI</sys:String>
            <sys:String>SUB_REGION</sys:String>
            <sys:String>POP2007</sys:String>
            <sys:String>SQMI</sys:String>
        </esri:FeatureLayer.OutFields>
    </esri:FeatureLayer>
    
  2. Include an XML namespace reference for the ESRI.ArcGIS.Client.Toolkit.Primitives namespace as an attribute on the phone:PhoneApplicationPage element.
    xmlns:esriPrimitives="clr-namespace:ESRI.ArcGIS.Client.Toolkit.Primitives;assembly=ESRI.ArcGIS.Client.Toolkit"
    
  3. In the ContentPanel, following the InfoWindow, add a ChildPage to use to display details. Set IsOpen to False, as done previously with the InfoWindow, since it should not display initially.
    <esriPrimitives:ChildPage x:Name="detailsPage" IsOpen="False">
    </esriPrimitives:ChildPage>
    
  4. Populate the ChildPage with the details to display. As with the information on the MapTip, bind to the attributes of the feature tapped and held.
    <esriPrimitives:ChildPage x:Name="detailsPage" IsOpen="False">
        <StackPanel Margin="5">
            <TextBlock Text="{Binding Attributes, 
                Converter={StaticResource MyDictionaryConverter},
                ConverterParameter=STATE_NAME, Mode=OneWay}" FontWeight="Bold" />
            <TextBlock Text="{Binding Attributes, 
                Converter={StaticResource MyDictionaryConverter},
                ConverterParameter=SUB_REGION, Mode=OneWay}" FontWeight="Bold" />
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Population Density (2007): " />
                <TextBlock Text="{Binding Attributes,
                    Converter={StaticResource MyDictionaryConverter}, 
                    ConverterParameter=POP07_SQMI, Mode=OneWay}" />
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Population (2007): " />
                <TextBlock Text="{Binding Attributes,
                    Converter={StaticResource MyDictionaryConverter}, 
                    ConverterParameter=POP2007, Mode=OneWay}" />
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Area (sq mi): " />
                <TextBlock Text="{Binding Attributes,
                    Converter={StaticResource MyDictionaryConverter}, 
                    ConverterParameter=SQMI, Mode=OneWay}" />
            </StackPanel>
        </StackPanel>
    </esriPrimitives:ChildPage>
    
  5. Add a button to the InfoWindow MapTip, inserting an additional StackPanel to hold it, and create a click event on it to open the details page.
    <esriToolkit:InfoWindow x:Name="infoWinMapTip" 
        BorderBrush="Black" Background="LightYellow" Foreground="Black"
        Map="{Binding ElementName=MyMap}" IsOpen="False">
        <StackPanel Orientation="Horizontal">
            <StackPanel Margin="5">
                <TextBlock Text="{Binding Attributes, 
                    Converter={StaticResource MyDictionaryConverter},
                    ConverterParameter=STATE_NAME, Mode=OneWay}" 
                    FontWeight="Bold" />
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Population Density (2007): " />
                    <TextBlock Text="{Binding Attributes,
                        Converter={StaticResource MyDictionaryConverter}, 
                        ConverterParameter=POP07_SQMI, Mode=OneWay}" />
                    </StackPanel>
            </StackPanel>
            <Button x:Name="showDetails" Content=">"  Click="showDetails_Click" 
                Background="DarkGoldenrod" Foreground="Black" />
        </StackPanel>
    </esriToolkit:InfoWindow>
    
    Screen shot of an InfoWindow showing some state information and a details button.
  6. In the code-behind, implement the button's Click event. When the button is clicked, you want to show the details page and have it populated with information about the feature presented in the MapTip.
    private void showDetails_Click(object sender, RoutedEventArgs e)
    {
        detailsPage.DataContext = (sender as FrameworkElement).DataContext;
        detailsPage.IsOpen = true;
    }
    
    Screen shot of an InfoWindow with the details ChildPage sliding into view.

The application now has MapTips with details pages. The ChildPage uses the hardware Back button to return to the map, or you can add a button to the details page to close it.

Handling multiple selected graphics

Your application should handle showing all the information when the user's touch event covers multiple features. In the previous example, if there are multiple results at the location where the user touches and holds, only the first result's MapTip displays. In practice, the application should instead provide access to all the graphics selected. One way is to have an additional InfoWindow used for multiple results. In the following example, a second InfoWindow and ChildPage are used to report the number of features selected in the MapTip, then provide access to a list of the full results.

  1. Add an additional InfoWindow to the ContentPanel following the ChildPage created in the previous section. The InfoWindow shows the number of features that were selected.
    <esriToolkit:InfoWindow x:Name="infoWinMultiMapTip" 
        BorderBrush="Black" Background="LightYellow" Foreground="Black"
        Map="{Binding ElementName=MyMap}" IsOpen="False">
        <StackPanel Orientation="Horizontal" Margin="5">
            <TextBlock Text="{Binding Count}" FontWeight="Bold" />
            <TextBlock Text=" features selected."  />
            <Button x:Name="showMultiDetails" Content=">" 
                Click="showMultiDetails_Click" Background="DarkGoldenrod" 
                Foreground="Black" />
        </StackPanel>
    </esriToolkit:InfoWindow>
    
  2. Update the MapGesture event to open the new InfoWindow if more than a single result is selected. In it, also set the MapTip's DataContext to the full results.
    private void MyMap_MapGesture(object sender, Map.MapGestureEventArgs e)
    {
        if (e.Gesture == GestureType.Hold)
        {
            infoWinMapTip.IsOpen = false;
            infoWinMultiMapTip.IsOpen = false;
    
            List<Graphic> results = new List<Graphic>(e.DirectlyOver(10));
            if (results.Count > 0)
            {
                if (results.Count == 1)
                {
                    MyMap.PanTo(results[0].Geometry);
    
                    infoWinMapTip.DataContext = results[0];
                    infoWinMapTip.Anchor = results[0].Geometry.Extent.GetCenter();
                    infoWinMapTip.IsOpen = true;
                }
                else
                {
                    MyMap.PanTo(e.MapPoint);
                            
                    infoWinMultiMapTip.DataContext = results;
                    infoWinMultiMapTip.Anchor = e.MapPoint;
                    infoWinMultiMapTip.IsOpen = true;
                }
                e.Handled = true;
            }
        }
    }
    
    Screen shot of an InfoWindow showing multiple results.
  3. Add an additional ChildPage in the ContextPanel after the new InfoWindow just added. This ChildPage is viewed when the details button is clicked from one of the new, multiple results InfoWindows. It contains a ListBox that shows the for all the selected features. Each result in the list also has a button with a click event. The button uses the same event as the details button from an InfoWindow showing a single result, since the same information should display for this single result.
    <esriPrimitives:ChildPage x:Name="multiResultsPage" IsOpen="False">
        <Grid>
            <ListBox ItemsSource="{Binding}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <StackPanel Grid.Column="0">
                                <StackPanel Orientation="Horizontal">
                                    <TextBlock Text="Name: " />
                                    <TextBlock Text="{Binding Attributes, 
                                        Converter={StaticResource MyDictionaryConverter}, 
                                        ConverterParameter=STATE_NAME}" />
                                </StackPanel>
                                <StackPanel Orientation="Horizontal">
                                    <TextBlock Text="Population density (2007): " />
                                    <TextBlock Text="{Binding Attributes, 
                                        Converter={StaticResource MyDictionaryConverter}, 
                                        ConverterParameter=POP07_SQMI}" />
                                </StackPanel>
                            </StackPanel>
                            <Button x:Name="showDetailsFromMulti" Grid.Column="1" 
                                Content=">" Click="showDetails_Click" 
                                Background="DarkGoldenrod" Foreground="Black" /> 
                            <Rectangle VerticalAlignment="Bottom" Height="1" 
                                Fill="{StaticResource PhoneForegroundBrush}" 
                                Margin="0,0,5,0" Grid.ColumnSpan="2" />
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </esriPrimitives:ChildPage>
    
  4. Implement the Click event for the button on the InfoWindow for multiple features so that the details page listing all the features display.
    private void showMultiDetails_Click(object sender, RoutedEventArgs e)
    {
        multiResultsPage.DataContext = (sender as FrameworkElement).DataContext;
        multiResultsPage.IsOpen = true;
    }
    
    Screen shot of an InfoWindow with the mutliple results ChildPage sliding into view.

Tips for working with an InfoWindow

Following the steps in the previous sections, it is easy to create an application with MapTips. However, keep in mind the following key points when working with an InfoWindow:

1/23/2012