場所と写真を記録するプログラムを作って思い出のシーンを保存しよう
「構成マネージャ」の設定
この状態では、まだBing Maps SDKが使用できませんので、これを使用できるようにします。
まず、VS2012のメニューから「ビルド(B)/構成マネージャ(O)」と選択します。
「プラットフォーム」がAny CPUになっていますので、プルダウンメニューから、該当するプラットフォームを選択します。筆者の環境では×86を選択する必要がありました(図11)。
[閉じる]ボタンをクリックすると、ソリューションエクスプローラー内の「参照設定」にあった「Bing Maps for C#, C++, or Visual Basic 」と「Microsoft Visual C++ Runtime Package」の先頭の黄色いアイコンが消えています。
これでBing Maps SDKの使用が可能になりました。
次にBing Mapの表示されるページを作成します。
Windows ストア空白のページの作成(BingMapsShowPage.xaml)
VS2012のメニューの「プロジェクト(P)/新しい項目の追加(W)」と選択して、左に表示される項目からWindows ストアを選択します。
右に表示されるテンプレートから「空白のページ」を選択します。「名前(N):」にはBingMapsShowPage.xamlと指定して、[追加(A)]ボタンをクリックします。
コントロールの配置
表示されるデザイン画面の
次にツールボックスからScrollViewerコントロールを1個配置し、その子要素としてMapコントロールを配置します。書き出されるXAMLコードをリスト3のように編集します。レイアウトは図13のようになります。
リスト3 書き出され編集されたXAMLコード(BingMapsShowPage.xaml)
- (1) bmという名前空間を定義しています。
- (2)
要素を配置し、その子要素として、
<Page x:Class="Memories.BingMapsShowPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Memories" xmlns:bm="using:Bing.Maps" ■(1) xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <ScrollViewer HorizontalScrollBarVisibility="Visible"> <Grid> <bm:Map Credentials="Bing Maps Account Centerで取得したBing Maps Key" x:Name="myMap" /> ■(2) </Grid> </ScrollViewer> </Grid> </Page>
Bing Maps Keyの取得方法
Bing Mapsを使用するには下記URLのBing Maps Account Centerに行って専用のライセンスキーを取得する必要があります。
→ Bing Maps Account Center
Windows Live IDを持ってる方はサインイン(Sign In)します。IDを持っていない方は、「Create」からWindows Live IDを作成してください。ここでは筆者がサインインした状態として進めます(図14)。
表示される画面の左にあるCreate or view keysをクリックします(図15)。
Create keyの画面が表示されますので、必要な項目を入力してSubmitしてください。筆者は既にキーを持っていますので、下記にキーが表示されています(図13)。Key typeはBasicとなっています。BasicでPublic websiteの場合は、「アプリケーションが制限なしに利用され、500,000 のトランザクションの任意の種類の 12ヶ月の期間内を超えない、公開ウェブサイトです。」となっているようです。
各ライブラリとデバイスへのアクセス許可の設定
今回のサンプルは、「画像ライブラリ」にアクセスするため、画像ライブラリへのアクセス許可が必要になります。また、マイクやWebカメラへの使用許可や、現在位置を取得するため、「場所」へのアクセス許可も必要です。
ソリューションエクスプローラー内にpackage.appxmanifestというファイルがありますので、このファイルをダブルクリックします。「機能」タブをクリックして、必要項目にチェックを付けます(図17)。
次に、ソリューションエクスプローラー内のMainWindow.xamlを展開して表示される、MainWindow.xaml.vbをダブルクリックしてリスト4のコードを記述します。
ロジックコードを記述する
リスト4 (MainWindow.xaml.vb)
写真、オーディオ録音、ビデオなどをキャプチャするクラスを提供する、Windows.Media.Capture名前空間をインポートします。
Imports Windows.Media.Capture
ファイル、フォルダおよびアプリケーションの設定を管理するクラスの含まれる、Windows.Storage名前空間をインポートします。
Imports Windows.Storage
メディア形式のプロパティが形成される時に必要となるクラスの含まれる、Windows.Media.MediaProperties名前空間をインポートします。
Imports Windows.Media.MediaProperties
シーケンシャルアクセスストリームおよびランダムアクセスストリームに対する読み取りと書き込みをサポートするクラスの含まれる、Windows.Storage.Streams名前空間をインポートします。
Imports Windows.Storage.Streams
コンテキストメニューおよびメッセージダイアログのサポートを提供するクラスの含まれる、Windows.UI.Popups名前空間をインポートします。
Imports Windows.UI.Popups
地理的位置にアクセス可能にするクラスの含まれる、Windows.Devices.Geolocation名前空間をインポートします。
Imports Windows.Devices.Geolocation Public NotInheritable Class MainPage Inherits Page Dim myIndex As Integer
インデックスによってアクセスできる要素の読み取り専用コレクションを表すIstorageFile型のメンバ変数myPictureFilesを宣言します。
Dim myPictureFiles As IReadOnlyList(Of IStorageFile)
フォト、オーディオ録音およびビデオをキャプチャする、新しいMediaCaptureクラスのインスタンス、myMediaCaptureメンバ変数を宣言します。
Dim myMediaCapture As New MediaCapture Dim Index As Integer Dim saveBmp As BitmapImage
現在の地理的場所にアクセス可能にするGeolocatorクラス型のメンバ変数myGeolocatorメンバ変数を宣言します。
Dim myGeolocator As Geolocator Dim myLatitude As String Dim myLongitude As String Dim no As Integer = 0
ページがアクティブになった時の処理
ピクチャライブラリにアクセスします。CreateFolderAsyncメソッドでピクチャライブラリ内にMemoryImageといサブフォルダを作成します。CreationCollisionOption.OpenIfExistsと指定すると、同名フォルダが存在する場合はフォルダ名を返し、ない場合は新規に作成します。
GetFilesAsyncメソッドでMemoryImageサブフォルダ内のファイルを取得し、コレクション変数imgFileに格納します。
Countプロパティでファイルの個数を取得し、ファイルが存在する場合は、[写した場所を表示]ボタンの使用を可能にします。それ以外は使用を不可とします。
新しいGeolocatorのインスタンスmyGeolocatorオブジェクトを作成し、場所の精度レベルを設定するDesiredAccuracyプロパティにはPositionAccuracy.Highと指定して、もっとも精巧な精度を指定します。
AddHandlerステートメントで、場所が更新された時に発生するPositionChangedイベントにイベントハンドラを追加します。GridViewを表示状態にします。
InitializeAsyncメソッドでMediaCaptureを初期化します。
CaptureElementのSourceプロパティにMediaCaptureのインスタンスmyMediaCaptureオブジェクトを指定します。
StartPreviewAsyncメソッドでプレビューを開始します。Webカメラが表示されます。
ピクチャライブラリのPhotoImageサブフォルダ内の画像一覧を、GridViewに追加するAddPhotoプロシージャを実行します。
カメラが装備されていない場合は例外処理を行います。
非同期処理に行われるため、メソッドの先頭にAsyncを追加します。
Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) Try Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary Dim mySubFolder = Await myFolder.CreateFolderAsync("MemoryImage", CreationCollisionOption.OpenIfExists) Dim imgFile = Await mySubFolder.GetFilesAsync If imgFile.Count > 0 Then positionButton.IsEnabled = True Else positionButton.IsEnabled = False End If If myGeolocator Is Nothing = True Then myGeolocator = New Geolocator myGeolocator.DesiredAccuracy = PositionAccuracy.High End If AddHandler myGeolocator.PositionChanged, AddressOf myGeolocator_PositionChanged GridView1.Visibility = Windows.UI.Xaml.Visibility.Visible
Await myMediaCapture.InitializeAsync() CaptureElement1.Source = myMediaCapture Await myMediaCapture.StartPreviewAsync AddPhoto() Catch messageTextBlock.Text = "カメラが装備されておりません。" shutterButton.IsEnabled = False positionButton.IsEnabled = False Exit Sub End Try End Sub
ページが非アクティブになった時の処理
RemoveHandlerでPositionChangedイベント時のイベントハンドラを削除します。 Protected Overrides Sub OnNavigatedFrom(e As Navigation.NavigationEventArgs) RemoveHandler myGeolocator.PositionChanged, AddressOf myGeolocator_PositionChanged MyBase.OnNavigatedFrom(e) End Sub
場所が更新された時に発生する処理
現在の地理的位置に関連付けられた経度と緯度を、地理的位置の緯度および経度データを含む、Geocoordinateクラス型のmyCoordinate変数に格納します。
メンバ変数myLatitudeに現在の地理的緯度を格納します。メンバ変数myLongitudeに現在の地理的経度を格納します。
Private Sub myGeolocator_PositionChanged(sender As Geolocator, args As PositionChangedEventArgs) Dim myCoordinate As Geocoordinate = args.Position.Coordinate myLatitude = myCoordinate.Latitude myLongitude = myCoordinate.Longitude End Sub
ピクチャライブラリのMemoryImageサブフォルダ内に画像を追加し一覧で表示する処理
ピクチャライブラリにアクセスします。CreateFolderAsyncでMemoryImageサブフォルダを作成しますが、CreationCollisionOption.OpenIfExistsを指定することで、既にフォルダが存在する場合は、そのフォルダ名を返してくれます。フォルダが存在しない場合は新規に作成してくれます。CreationCollisionOption列挙体については下記URLを参照してください。
→ CreationCollisionOption列挙体
GetFilesAsyncメソッドでMemoryImageサブフォルダ内のファイルを取得し、ファイルのコレクションを表すメンバ変数myPictureFilesに格納しておきます。
コレクション変数myPictureFiles内のファイルを変数myPhotoFileに格納しながら、以下の処理を反復します。
新しいBitmapImageのインスタンスbmpオブジェクトを作成し、SetSourceメソッドに、Await myPhotoFile.OpenSequentialReadAsyncと指定して、アクセス用のストリームを順次開いていきます。
新しいImageのインスタンスmyImageオブジェクトを作成します。Widthに160、Heightに120と指定します。
ImageのSourceプロパティにストリームを開いているbmpオブジェクトを指定します。
新しいTextBlockのインスタンスmyFileNameを作成します。Nameプロパティでファイル名を取得しますが、絶対パスが付いていますので、SubString関数でファイル名だけを取得します。
新しいButtonのインスタンスmyButtonオブジェクトを作成します。
Contentプロパティに「削除」と指定し、Tagプロパティに1ずつ加算されるメンバ変数noの値を文字列に変換して指定します。このTagプロパティの値は、どのボタンがタップされたかの判別に使用します。
新しいStackPanelのインスタンスmyStackPanelオブジェクトを作成します。
Marginプロパティに3を指定して余白を設けます。
AddメソッドでmyImageオブジェクトを追加し、次にmyFileNameオブジェクトを追加します。
次にmyButtonオブジェクトを追加します※。
これらのオブジェクトの追加されたmyStackpanelオブジェクトをGridViewに追加します。
これで、GridViewの中に画像とファイル名と[削除]ボタンが表示されます。
(※)このように画像にボタンを追加して表示する処理は、Windowsストアの作法として実は好ましくありません。筆者もこの原稿を書いた時点では、Windowsストアの作法を熟知していなかったため、画像に1つずつ「削除」ボタンを追加してしましました。 こういった場合は。アプリケーションバー(AppBar)内に「削除」アイコンを表示させて、そのアイコンをタップすることで、選択された画像が削除されるようにする、といった方法が望ましいです。
AddHandlerステートメントでmyButtonがタップされた時のイベントハンドラを追加します。イベントハンドラ内では以下の処理が行われます。
変数myNoに、Buttonの情報を保持しているdelSederオブジェクトをButtonにキャストして、そのTagプロパティの値を取得して格納します。
変数myIndexに変数myNoの値を数値に変換して格納します。
コレクション変数myPictureFiles内で変数myIndexに該当するファイルをDeleteAsyncメソッドで削除します。
「削除しました。」のメッセージを表示します。
GridView内をクリアし、メンバ変数noを0で初期化して、再度AddPhotoプロシージャを実行します。
GridView内が更新されます。
メンバ変数Indexに、コレクション変数myPictureFiles内のファイルの個数をCountプロパティで取得し、格納します。
ファイルがない場合は処理を抜けます。それ以外の場合は、GridViewの選択されたインデックスにメンバ変数Indexから-1した値を指定します。-1するのは、GridViewのSelectedIndexは0から始まるためです。これで、今現在撮影した写真が選択状態になります。
ScrollIntoViewメソッドで、選択された画像が表示されるようスクロールします。
非同期処理に行われるため、メソッドの先頭にAsyncを追加します。
Private Async Sub AddPhoto() no = 0 Try Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary Dim mySubFolder = Await myFolder.CreateFolderAsync("MemoryImage", CreationCollisionOption.OpenIfExists) myPictureFiles = Await mySubFolder.GetFilesAsync() If myPictureFiles.Count = 0 Then Image1.Source = Nothing For Each myPhotoFile In myPictureFiles Dim bmp As New BitmapImage bmp.SetSource(Await myPhotoFile.OpenSequentialReadAsync) Dim myImage As Image = New Image myImage.Width = 160 myImage.Height = 120 myImage.Source = bmp Dim myFileName As New TextBlock myFileName.Text = myPhotoFile.Name.Substring(0, 20) myFileName.FontSize = 10 myFileName.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Center Dim myButton As New Button With myButton .Content = "削除" .FontSize = 18 .Tag = no.ToString .HorizontalAlignment = Xaml.HorizontalAlignment.Center End With Dim myStackPanel As StackPanel = New StackPanel myStackPanel.Margin = New Thickness(3) myStackPanel.Children.Add(myImage) myStackPanel.Children.Add(myFileName) myStackPanel.Children.Add(myButton) GridView1.Items.Add(myStackPanel) AddHandler myButton.Click, Async Sub(delSender As Object, delArgs As RoutedEventArgs) Dim myNo As String = DirectCast(delSender, Button).Tag Dim myIndex As Integer = CInt(myNo) Await myPictureFiles(myIndex).DeleteAsync Dim myMessage As New MessageDialog("削除しました。") Await myMessage.ShowAsync GridView1.Items.Clear() no = 0 AddPhoto() End Sub no += 1 Next myPictureFiles = Await mySubFolder.GetFilesAsync() Index = myPictureFiles.Count If Index = 0 Then Exit Sub Else GridView1.SelectedIndex = Index - 1 GridView1.ScrollIntoView(GridView1.SelectedItem) End If Catch Exit Sub End Try End Sub
思い出の写真を記録するサンプルプログラム