My Photo Map Appの開発
前回はBing Map Appのコンテストで受賞・
さて今回と次回は、
- ユーザーの写真を地図上とパネルに表示
- 写真のサムネイルと経緯度情報を取得して使用
- パネルの写真を選択するとその場所に移動
- プッシュピンをクリックすると写真をポップアップ表示
- 追加した写真情報の保存
- ID
(文字列) - 名前
(文字列) - 写真
(BitmapImgeクラス) - ListBoxの項目のスタイル
- ListBoxの項目内のリンクのスタイル
- ListBoxの項目のコントロールの構成
- Click="HyperlinkButton_
Click" - Drop="PhotoListBox_
Drop" - Click="Button_
Click"

ユーザーのPCからの写真の読み込みや、
プラグインの作成
それでは、
namespace MyPhotoMapApp
{
using Microsoft.Maps.Core;
using Microsoft.Maps.Plugins;
using Microsoft.Maps.MapControl;
public class MyPhotoPlugin : Plugin
{
[ImportSingle("Microsoft/MapContract", ImportLoadPolicy.Synchronous)]
public MapContract DefaultMap { get; set; }
[ImportSingle("Microsoft/LayerManagerContract", ImportLoadPolicy.Synchronous)]
public LayerManagerContract LayerManagerContract { get; set; }
[ImportSingle("Microsoft/PushpinFactoryContract", ImportLoadPolicy.Synchronous)]
public PushpinFactoryContract PushpinFactoryContract { get; set; }
[ImportSingle("Microsoft/PopupContract", ImportLoadPolicy.Synchronous)]
public PopupContract PopupContract { get; set; }
public MyPhotoLayer MainLayer { get; set; }
public override void Initialize()
{
base.Initialize();
this.MainLayer = new MyPhotoLayer(this.Token, this);
}
public override void Activate(System.Collections.Generic.IDictionary<string, string> activationParameters)
{
if (LayerManagerContract.ContainsLayer(this.MainLayer))
{
LayerManagerContract.BringToFront(this.MainLayer);
}
else
{
LayerManagerContract.AddLayer(this.MainLayer);
}
}
}
}
namespace MyPhotoMapApp
{
using Microsoft.Maps.Core;
using Microsoft.Maps.Plugins;
using System;
public class MyPhotoLayer : Layer
{
private MyPhotoPlugin plugin;
public MyPhotoLayer(PluginToken pluginToken, MyPhotoPlugin plugin)
: base(pluginToken)
{
this.plugin = plugin;
this.Title = "My Photo";
var panel = new MyPhotoPanel(plugin);
this.Panel = panel;
}
}
}
パネルとなるSilverlightユーザーコントロールもプロジェクトに追加します。今回のアプリの方針として、
地図の参照やプッシュピンの追加などContractによって提供されている機能や、
namespace MyPhotoMapApp
{
using Microsoft.Maps.Core;
using Microsoft.Maps.Plugins;
using Microsoft.Maps.Network;
using Microsoft.Maps.MapControl;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
public partial class MyPhotoPanel : UserControl
{
private MyPhotoPlugin plugin;
public MyPhotoPanel(MyPhotoPlugin plugin)
{
InitializeComponent();
this.plugin = plugin;
}
}
}
MyPhotoPanel.
PhotoEntityクラス
次に、
位置情報は、
namespace MyPhotoMapApp
{
using Microsoft.Maps.Core;
using System.Windows.Media.Imaging;
using System;
public class PhotoEntity : Entity
{
public string Id { get; set; }
public BitmapImage BitmapImage { get; set; }
public string Name
{
get
{
return (string)base.GetValue(RetrieveProperty("Core.Name", typeof(string)));
}
set
{
base.SetValue(RetrieveProperty("Core.Name", typeof(string)), value);
}
}
}
}
上記コードではNameプロパティで少し特殊なことをしているので説明します。エンティティの名前というのは、
Map Appにおいて、
このPhotoEntityクラスは、
private MyPhotoPlugin plugin;
private ObservableCollection<PhotoEntity> PhotoItems; // 追加
public MyPhotoPanel(MyPhotoPlugin plugin)
{
InitializeComponent();
this.plugin = plugin;
this.PhotoItems = new ObservableCollection<PhotoEntity>(); // 追加
}
パネルのデザイン
次にパネルの見た目の部分を作成していきましょう。パネル上には、

XAMLの編集
順にMyPhotoPanel.
<UserControl x:Class="MyPhotoMapApp.MyPhotoPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="PhotoListBox"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"
ItemTemplate="{StaticResource ListBoxItemTemplate}"
AllowDrop="True" Drop="PhotoListBox_Drop">
<ListBox.Template>
<ControlTemplate>
<ScrollViewer Style="{StaticResource App.ScrollViewer}">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
<Button Grid.Row="1" Content="Add photo" Width="120" Margin="5"
Style="{StaticResource App.Button1}"
Click="Button_Click" />
</Grid>
</UserControl>
StaticResource部分で指定している、
内容は特に気にせず次のコードをコピーしていきましょう。それぞれ、
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="selectedItem"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected" />
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused" />
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="selectedItem" Style="{StaticResource App.Border.Rollover}" Opacity="0">
<Rectangle Fill="{StaticResource App.Color.ItemRollover}" IsHitTestVisible="False" RadiusY="1" RadiusX="1"/>
</Border>
<ContentPresenter x:Name="contentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="HyperlinkButtonStyle" TargetType="HyperlinkButton" BasedOn="{StaticResource App.P1.Hyperlink}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="HyperlinkButton">
<Grid Background="{TemplateBinding Background}" Cursor="{TemplateBinding Cursor}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="UnderlineTextBlock">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="contentPresenter">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="UnderlineTextBlock">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="contentPresenter">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused" />
<VisualState x:Name="Unfocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock x:Name="UnderlineTextBlock"
TextDecorations="Underline"
Visibility="Collapsed"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
Text="{TemplateBinding Content}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<ContentPresenter x:Name="contentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="ListBoxItemTemplate">
<ListBoxItem Style="{StaticResource ListBoxItemStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Width="60" BorderBrush="LightGray" BorderThickness="1" Background="White" Padding="2" RenderTransformOrigin="0.5,0.5" Margin="10">
<Border.RenderTransform>
<CompositeTransform Rotation="-10"/>
</Border.RenderTransform>
<Border.Effect>
<DropShadowEffect BlurRadius="5" ShadowDepth="1" Color="Gray" Direction="250" />
</Border.Effect>
<Image Source="{Binding BitmapImage}" />
</Border>
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<HyperlinkButton Content="{Binding Name}" Style="{StaticResource HyperlinkButtonStyle}"
Click="HyperlinkButton_Click" />
</StackPanel>
</Grid>
</ListBoxItem>
</DataTemplate>
デザイン部分は以上です。見た目を少しよくするためにコードが長くなっています。上記部分にもSDKで用意されいてるスタイル
これらをすべてVisual Studioで記載したわけではなく、
ListBoxの項目の構成を記述しているListBoxItemTemplateでは、
項目を構成するコントロールの中のHyperlinkButtonについても、
イベントの仮処理
上記のXAMLのコードには、
コピーした場合はこれらに対応する処理がないため実行時にエラーになってしまいます。今回は何もしない空のメソッドをコードビハインドに記述しておきましょう。記述するといっても自分で書くのではなくVisual Studioの機能を利用してください。デザイナのメニュー

正しく追加された場合は、
private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
}
private void PhotoListBox_Drop(object sender, DragEventArgs e)
{
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
デザインの確認
実際の処理部分は次回に続きますが、
public MyPhotoPanel(MyPhotoPlugin plugin)
{
InitializeComponent();
this.plugin = plugin;
this.PhotoItems = new ObservableCollection<PhotoEntity>();
this.PhotoListBox.ItemsSource = this.PhotoItems;
// 一時的なコード
for (var i = 0; i < 10; ++i)
{
var item = new PhotoEntity()
{
Name = "写真" + i.ToString(),
BitmapImage = new BitmapImage(new Uri("http://image.gihyo.co.jp/assets/images/ICON/2010/thumb/TH64_641_bing-sdk.png"))
};
this.PhotoItems.Add(item);
}
}
実行結果は図4の通りです。Bing Mapsのようなデザインになったでしょうか?

今回はここまでです。次回はアプリの動作を記述していきます。お楽しみに。