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:
- Add a DictionaryConverter to your application as a resource.
- Create an InfoWindow to display the MapTip information, including binding it to the map.
- Provide a map and an anchor for the InfoWindow.
- Handle the gestures on the map to display and hide the InfoWindow MapTips.
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:
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.
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.
- 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.The following code shows the ContentPanel element's XAML:
<Grid.Resources> <esriSymbols:SimpleFillSymbol x:Key="MyFillSymbol" Fill="#66FF0000" BorderBrush="Red" BorderThickness="2" /> <esri:SimpleRenderer x:Key="MySimpleRenderer" Symbol="{StaticResource MyFillSymbol}" /> </Grid.Resources>
For additional details, see Creating a map, Feature layers, and Symbols and renderers.<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>
Note:- 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"
- Add a reference to the ESRI.ArcGIS.Client.Toolkit assembly, which contains the InfoWindow.
- 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"
- 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>
- 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>
- 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>
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"
- 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>
- 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>
- 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>
- 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>
- Add a MapGesture event handler to the map.
<esri:Map x:Name="MyMap" Extent="-130, 10, -70, 60" MapGesture="MyMap_MapGesture">
- 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;
- 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; } }
- 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; } } }
- Run your application. Tap and hold a state to see the MapTip. See the following screen shot:
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:
- 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>
- 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"
- 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>
- 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>
- 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>
- 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; }
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.
- 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>
- 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; } } }
- 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>
- 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; }
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:
- An InfoWindow requires that its Map and Anchor properties are set before it can open. Without them, it doesn't know what map it is attached to, or where on that map to appear.
- Once the InfoWindow has a Map and an Anchor, it can be shown and hidden using its IsOpen Boolean property.
- A margin on an InfoWindow provides an offset from the anchor of the InfoWindow. If you have a graphic that was selected, and you want your InfoWindow to appear from the top of that graphic, the Margin property can be used to place the InfoWindow so that it appears in the correct location relative to the graphic.
- While a common workflow is to pair the InfoWindow with a ChildPage, you can use any controls in conjunction with the InfoWindow to create a custom experience.