Using the InfoWindow to create map tips

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 map tips to your ArcGIS 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 map tips so that the information displayed is always based on the attributes of the current graphic. To add map tips to an application that already contains one or more graphics layers, you need to:

The InfoWindow is a control available as part of the ESRI.ArcGIS.Client.Toolkit assembly, designed to give an easy "popup" experience in Windows Phone 7 applications. Another common user experience is to have additional information available off of a map tip. Another control, the ChildPage, available in the ESRI.ArcGIS.Client.Toolkit.Primitives assembly, helps create that experience. This topic builds an example application with map tips, and then provides some tips for working with an InfoWindow.

Building an application with map tips

This section will detail all the steps for building an application with map tips. It includes creating an additional information page accessible from the map tip, as well as handling when the user clicks on both single and multiple features. The following steps are used to create the application:

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

Adding an InfoWindow showing map tips

An example application with InfoWindow map tips 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 map tip showing the state's name and population density is displayed.

NoteNote:

Map tips 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 base map layer, a FeatureLayer, and a symbol for the FeatureLayer. The FeatureLayer should only display features with a population density over 150 and should use the MyFillSymbol resource. The Grid.Resource XAML creating MyFillSymbol in the LayoutRoot element and the ContentGrid element's XAML are shown below. For additional detail, see Creating a map, Feature layers, and Working with symbols and renderers.
    <Grid.Resources>
        <esriSymbols:SimpleFillSymbol x:Name="MyFillSymbol" 
            Fill="#66FF0000" BorderBrush="Red" BorderThickness="2" />
    </Grid.Resources>
    
    <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" 
                FeatureSymbol="{StaticResource MyFillSymbol}" >
                <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>
    
    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 both 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 inside the ContentPanel element, add an InfoWindow element. The map tip's display will be defined inside this element. Provide an x:Name value for the element, as well as binding it to the map it will interact with. Specify that IsOpen is 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 map tip's content. In the code here, a StackPanel is used. The margin is specified on the StackPanel, The InfoWindow will automatically resize 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 map tip the name of the state being tapped and held, you need to 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 will be available for binding. The most straightforward 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, you must first 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 LayoutGrid element.
    <Grid.Resources>
        <x:Name="MyFillSymbol" Fill="#66FF0000" BorderBrush="Red" BorderThickness="2" />
        <esriConverters:DictionaryConverter x:Name="MyDictionaryConverter" />
    </Grid.Resources>
    
  9. Within the InfoWindow element, the DataContext (what data binding will try to attach to) is the graphic that has been tapped and held. It's 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.
    <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 a GestureType.Hold, set IsOpen to false to close any open map tips.
    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 then display its map tip 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 map tip.
    Screen shot of an InfoWindow showing some state information.

Adding a details page

Often map tips 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 map tip 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 who's contents are completely up to you as the developer. Building on the map tips 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" 
        FeatureSymbol="{StaticResource MyFillSymbol}" >
        <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 we did 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 map tip, 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 map tip, 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, we want to show the details page and have it populated with information about the feature presented in the map tip.
    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 map tips with details pages. The ChildPage makes use of the hardware back button to get back to the map, or you could add a button to the details page for closing it.

Handling multiple selected graphics

Your application should handle showing all the information when the user's touch event covers multiple features. In the example code above, if there are multiple results at the location where the user touches and holds, only the first result's map tip is displayed. 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 map tip, and 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 will show 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 map tip'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 will be viewed when the details button is clicked from one of the new, multiple results InfoWindows. It will contain a ListBox that shows the map tips 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 be displayed 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 is displayed.
    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 section above, it is easy to create an application with map tips. However, there are a few key points to keep in mind when working with InfoWindows:


12/1/2010