I checked fast if I could make this work in ItemsControl but didn't figure right away if that is possible. One solution that you can use here is to make AttachedProperty that takes ObservableCollection<YourModel/Graphic/WhatEver> and on bind changes creates / removes InfoWindows from /to Map's parent panel (assuming you have that kind of structure). This is not the cleanest way to handle this kind of problem but with fast test seems to work more or less. Here is some test code that you could use to implement this kind of solution.Here is rough structure how to make this happen:
public class InfoWindowManager : DependencyObject
{
// Contains mapping between the graphic and infowindow
private static readonly List<Tuple<Graphic, InfoWindow>> itemMap = new List<Tuple<Graphic, InfoWindow>>();
// Contains reference to map
private static Map map;
// Panel where map is located
private static Panel parentPanel;
// Dependency property to work bind collection of Graphics that are assosiated with info window
public static readonly DependencyProperty InfoWindowsProperty =
DependencyProperty.RegisterAttached(
"InfoWindows",
typeof(ObservableCollection<Graphic>),
typeof(InfoWindowManager),
new PropertyMetadata(OnInfoWindowsPropertyChanged));
public static void SetInfoWindows(UIElement element, ObservableCollection<Graphic> value)
{
element.SetValue(InfoWindowsProperty, value);
}
public static ObservableCollection<Graphic> GetInfoWindows(UIElement element)
{
return (ObservableCollection<Graphic>)element.GetValue(InfoWindowsProperty);
}
// Map colleciton binding
private static void OnInfoWindowsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var targetMap = d as Map;
if (targetMap != null)
{
map = targetMap;
parentPanel = map.Parent as Panel;
}
var oldList = e.OldValue as ObservableCollection<Graphic>;
if (oldList != null)
{
oldList.CollectionChanged -= OnCollectionChanged;
}
var newList = e.NewValue as ObservableCollection<Graphic>;
if (newList != null)
{
newList.CollectionChanged += OnCollectionChanged;
// Add items that are in the collection on bind time
foreach (var item in newList)
{
AddItem(item);
}
}
}
// Occurs when binded collection is changed
private static void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
var itemsToRemove = parentPanel.Children.OfType<InfoWindow>().ToList();
if (itemsToRemove.Count > 0)
{
itemsToRemove.ForEach(x => parentPanel.Children.Remove(x));
itemMap.Clear();
}
}
if (e.NewItems != null)
{
// Add new items
foreach (Graphic item in e.NewItems)
{
AddItem(item);
}
}
if (e.OldItems != null)
{
// Remove all old items.
foreach (Graphic item in e.OldItems)
{
var info = itemMap.FirstOrDefault(x => x.Item1 == item);
if (info != null)
{
parentPanel.Children.Remove(info.Item2);
itemMap.Remove(info);
}
}
}
}
private static void AddItem(Graphic itemToAdd)
{
// Create infowindow and set properties
var info = new InfoWindow
{
Anchor = itemToAdd.Geometry as MapPoint,
IsOpen = true,
Content = itemToAdd.Attributes,
Map = map
};
// Add infowindow to layout structure
parentPanel.Children.Add(info);
// Add mapping with graphic and infowindow
itemMap.Add(new Tuple<Graphic, InfoWindow>(itemToAdd, info));
}
}
And in the xaml you can bind to this like this. Note that in this case i have ViewModel with ObservableCollection<Graphic> and I bind those to GraphicsLayer using DataContextProxy class that I used in http://forums.arcgis.com/threads/63189-GpsLayer-and-MVVM-issues example. Here you might want to have 2 separate lists, another to graphics to draw and another with graphics that contains those who should have infowindow opened.
<Grid>
<Grid.Resources>
<local:DataContextProxy x:Key="proxy" Data="{Binding}" />
<Style TargetType="{x:Type esri:InfoWindow}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding [CREATED]}" Foreground="Red" FontSize="12" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<esri:Map local:InfoWindowManager.InfoWindows="{Binding SearchResults}">
<esri:ArcGISTiledMapServiceLayer ID="PhysicalTiledLayer"
Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"/>
<esri:GraphicsLayer ID="MyGraphicsLayer"
GraphicsSource="{Binding Path=Data.SearchResults, Source={StaticResource proxy}}"
Renderer="{StaticResource MySimpleRenderer}" />
</esri:Map>
</Grid>
Not sure if this helps but atleast I had fun creating it 🙂Edit: Added image from test solution[ATTACH=CONFIG]16538[/ATTACH]