Select to view content in your preferred language

Showing multiple infowindows at the same time

3431
1
07-11-2012 11:54 PM
Labels (1)
ae
by
Frequent Contributor
Hi,

Is it somehow possible to show multiple infowindows simultaneously?

I tried adding an itemscontrol with infowindows to my xaml, but when I run it none of the infowindows shows up. If I simply replace the datatemplate in the itemscontrol with button instead of infowindows it works straight away.

I know that all the bindings are working, so that's not the problem.

The code for the itemscontrol is included below:

 <ItemsControl x:Name="InfoWindows2" ItemsSource="{Binding Resources, Source={StaticResource mvm} }" >
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <esri:InfoWindow IsOpen="True" Anchor="{Binding MapPoint}" Height="300px"
                         Width="300px"
                         Background="Black"
                         Foreground="White"
                         Map="{Binding ElementName=MyMap}" 
                          ContentTemplate="{StaticResource ResourceLayerInfoWindowTemplate}"
                         Content="{Binding Resources, Source={StaticResource mvm}}">
                                </esri:InfoWindow>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>


If it is impossible to show multiple infowindows at the same time, is there some other control I could use?

All answers are greatly appreciated! 🙂
0 Kudos
1 Reply
AnttiKajanus1
Deactivated User
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]
0 Kudos