В этой статье я продолжу обзор использования
Bing Maps в приложениях Windows Store. Но на этот раз я расскажу как стилизовать Pushpin-объекты на карте для различных типов объектов. Сразу приведу результат программы, чтобы было понятнее о чем идет речь.
Как видно, на карте расположено несколько объектов различного типа (магазины, кинотеатры, POI). Все эти объекты находятся в одной коллекции
MapItemsControl объекта
Map.
MainPage.xaml:
<common:LayoutAwarePage x:Class="WpfCsharp.BingMaps.MainPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="using:WpfCsharp.BingMaps.Common"
xmlns:selector="using:WpfCsharp.BingMaps.Selector"
xmlns:maps="using:Bing.Maps">
<common:LayoutAwarePage.Resources>
<CollectionViewSource x:Name="collectionViewSource" Source="{Binding Items}"/>
<selector:MapItemTemplateSelector x:Key="mapItemTemplateSelector" DefaultTemplateKey="MapTemplate" IsCacheEnabled="True"/>
</common:LayoutAwarePage.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<maps:Map x:Name="map" Credentials="{YOU MAP KEY}">
<maps:MapItemsControl ItemsSource="{Binding Source={StaticResource collectionViewSource}}" ItemTemplateSelector="{StaticResource mapItemTemplateSelector}"/>
</maps:Map>
</Grid>
</common:LayoutAwarePage>
Данная коллекция наследуется от
ItemsControl и соответственно имеет свойство
ItemTemplateSelector, с помощью которого мы определим собственную логику выбора шаблона для каждого отображаемого элемента:
MapItemTemplateSelector.cs:
public class MapItemTemplateSelector : DataTemplateSelector
{
#region Fields
private Dictionary<String, DataTemplate> cachedDataTemplates;
#endregion
#region Properties
public string DefaultTemplateKey { get; set; }
public bool IsCacheEnabled { get; set; }
#endregion
#region DataTemplateSelector Members
protected override DataTemplate SelectTemplateCore(Object item, DependencyObject container)
{
if (item == null) return null;
var key = String.Concat(item.GetType().Name, DefaultTemplateKey);
var dataTemplate = GetCachedDataTemplate(key);
if (dataTemplate != null) { return dataTemplate; }
try
{
var frameworkElement = container as FrameworkElement;
while (frameworkElement != null)
{
dataTemplate = FindTemplate(frameworkElement, key);
if (dataTemplate != null) { return dataTemplate; }
frameworkElement = VisualTreeHelper.GetParent(frameworkElement) as FrameworkElement;
}
dataTemplate = FindTemplate(null, key);
return dataTemplate;
}
finally
{
if (dataTemplate != null)
{
AddCachedDataTemplate(key, dataTemplate);
}
}
}
#endregion
#region Private Members
private DataTemplate GetCachedDataTemplate(string key)
{
if (!IsCacheEnabled) { return null; }
VerifyCachedDataTemplateStorage();
return cachedDataTemplates.ContainsKey(key) ? cachedDataTemplates[key] : null;
}
private void AddCachedDataTemplate(string key, DataTemplate dt)
{
if (!IsCacheEnabled) { return; }
VerifyCachedDataTemplateStorage();
cachedDataTemplates[key] = dt;
}
private void VerifyCachedDataTemplateStorage()
{
if (cachedDataTemplates == null)
{
cachedDataTemplates = new Dictionary<string, DataTemplate>();
}
}
private static DataTemplate FindTemplate(object source, string key)
{
var frameworkElement = source as FrameworkElement;
var resourceDictionary = frameworkElement == null ? Application.Current.Resources : frameworkElement.Resources;
object value;
if (resourceDictionary.TryGetValue(key, out value))
{
var dataTemplate = value as DataTemplate;
if (dataTemplate != null) return dataTemplate;
}
return null;
}
#endregion
}
Данный класс позволяет найти визуальную структуру объекта данных начиная поиск с ресурсов текущего элемента управления и завершая в ресурсах всего приложения. Он также может кэшировать шаблоны (
свойство IsCacheEnabled) для более быстрого поиска нужной визуальной структуры. Поиск проводится по имени типа объекта с добавлением ключа по умолчанию (
DefaultTemplateKey). Т.е. если объекты на карте в коде выглядят следующим образом:
Place.cs:
public abstract class Place
{
public String Title { get; set; }
public Double Latitude { get; set; }
public Double Longitude { get; set; }
}
public class Shop : Place { }
public class Poi : Place { }
public class Cinema : Place { }
то их шаблоны (
с учетом того, что DefaultTemplateKey = MapTemplate) будут выглядеть так:
MainPage.xaml:
<DataTemplate x:Key="CinemaMapTemplate">
<maps:Pushpin Width="32" Height="32" Tapped="OnPushpinTapped">
<maps:MapLayer.Position>
<maps:Location Latitude="{Binding Latitude}" Longitude="{Binding Longitude}"></maps:Location>
</maps:MapLayer.Position>
<maps:Pushpin.Template>
<ControlTemplate>
<Grid RenderTransformOrigin="0.5,0.5">
<Image Source="/Assets/cinema.png" Width="32" Height="32" />
<Grid.RenderTransform>
<TranslateTransform X="-16" Y="-16"/>
</Grid.RenderTransform>
</Grid>
</ControlTemplate>
</maps:Pushpin.Template>
</maps:Pushpin>
</DataTemplate>
<DataTemplate x:Key="PoiMapTemplate">
<maps:Pushpin Width="32" Height="32" Tapped="OnPushpinTapped">
<maps:MapLayer.Position>
<maps:Location Latitude="{Binding Latitude}" Longitude="{Binding Longitude}"></maps:Location>
</maps:MapLayer.Position>
<maps:Pushpin.Template>
<ControlTemplate>
<Grid RenderTransformOrigin="0.5,0.5">
<Image Source="/Assets/poi.png" Width="32" Height="32" />
<Grid.RenderTransform>
<TranslateTransform X="-16" Y="-16"/>
</Grid.RenderTransform>
</Grid>
</ControlTemplate>
</maps:Pushpin.Template>
</maps:Pushpin>
</DataTemplate>
<DataTemplate x:Key="ShopMapTemplate">
<maps:Pushpin Width="32" Height="32" Tapped="OnPushpinTapped">
<maps:MapLayer.Position>
<maps:Location Latitude="{Binding Latitude}" Longitude="{Binding Longitude}"></maps:Location>
</maps:MapLayer.Position>
<maps:Pushpin.Template>
<ControlTemplate>
<Grid RenderTransformOrigin="0.5,0.5">
<Image Source="/Assets/shop.png" Width="32" Height="32" />
<Grid.RenderTransform>
<TranslateTransform X="-16" Y="-16"/>
</Grid.RenderTransform>
</Grid>
</ControlTemplate>
</maps:Pushpin.Template>
</maps:Pushpin>
</DataTemplate>
И чтобы получить тот результат, который я привел в самом начале статьи, создадим коллекцию объектов Place и добавим в нее несколько объектов:
MainPage.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Items = new ObservableCollection<Place>();
DefaultViewModel["Items"] = Items;
Items.Add(new Shop { Title = "Shop №1", Latitude = 55.801843, Longitude = 37.531883 });
Items.Add(new Shop { Title = "Shop №2", Latitude = 55.748922, Longitude = 37.538284 });
Items.Add(new Shop { Title = "Shop №3", Latitude = 55.748922, Longitude = 37.565991 });
Items.Add(new Shop { Title = "Shop №4", Latitude = 55.845758, Longitude = 37.665674 });
Items.Add(new Shop { Title = "Shop №5", Latitude = 55.754775, Longitude = 37.621324 });
Items.Add(new Cinema { Title = "Cinema №1", Latitude = 55.766203, Longitude = 37.607514 });
Items.Add(new Cinema { Title = "Cinema №2", Latitude = 55.7436842, Longitude = 37.508058 });
Items.Add(new Cinema { Title = "Cinema №3", Latitude = 55.726944, Longitude = 37.579712 });
Items.Add(new Poi { Title = "Poi №1", Latitude = 55.680868, Longitude = 37.601534 });
Items.Add(new Poi { Title = "Poi №2", Latitude = 55.677425, Longitude = 37.695776 });
Items.Add(new Poi { Title = "Poi №3", Latitude = 55.706514, Longitude = 37.678524 });
Items.Add(new Poi { Title = "Poi №4", Latitude = 55.774617, Longitude = 37.753170 });
Items.Add(new Poi { Title = "Poi №5", Latitude = 55.656901, Longitude = 37.554735 });
}
Это все интересно, но куда интересней предоставить пользователю более подробную информацию об объекте на карте когда он выберет тот или иной объект. Для этого в шаблонах я подписался на событие
Tapped, в обработке которого я буду заменять выделенный объект другим:
MainPage.xaml.cs:
private void OnPushpinTapped(object sender, TappedRoutedEventArgs e)
{
var pin = sender as Pushpin;
if(pin == null) return;
var place = pin.DataContext as Place;
if(place == null) return;
var index = Items.IndexOf(place);
if(index > 0)
{
Items[index] = new Balloon(place);
}
}
Объект Balloon также наследуется от класса Place, но будет имеет немного другую визуальную структуру:
MainPage.xaml:
<DataTemplate x:Key="BalloonMapTemplate">
<maps:Pushpin Text="{Binding Title}" Width="256" Height="128">
<maps:MapLayer.Position>
<maps:Location Latitude="{Binding Latitude}" Longitude="{Binding Longitude}"></maps:Location>
</maps:MapLayer.Position>
<maps:Pushpin.Template>
<ControlTemplate>
<Grid x:Name="ContentGrid" RenderTransformOrigin="0.5,0.5">
<Path Width="256" Height="128" Stretch="Fill" Fill="WhiteSmoke" Stroke="Black" StrokeThickness="1"
Data="F1 M 33,51L 36.4167,61.75L 24,51L 19,51L 19,22L 57,22L 57,51L 33,51 Z "/>
<ContentPresenter RenderTransformOrigin="0.5,0.5" Content="{Binding Title}" Foreground="Black" FontSize="24"
VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" Margin="0,-30,0,0"/>
<Grid.RenderTransform>
<TranslateTransform X="-120" Y="-128"/>
</Grid.RenderTransform>
</Grid>
</ControlTemplate>
</maps:Pushpin.Template>
</maps:Pushpin>
</DataTemplate>
Этот шаблон не что иное как облако, которое содержит название объекта. Тем самым окончательный результат будет выглядеть немного изящней:
Исходный код
Комментариев нет:
Отправить комментарий