撮影した写真の管理ができるマイフォトアプリを作る

2013年8月31日(土)
薬師寺 国安

「Attach Camera」アイコンがタップされた時の処理

MediaElement1を再生します。つまりシャッター音が鳴ります。

ピクチャライブラリにアクセスし、CreateFolderAsyncメソッドで、ピクチャフォルダ内にmyPictureAlbumというサブフォルダを作成します。その際、CreationCollisionOption.OpenIfExistsと指定しておくと、同名フォルダやファイルがある場合は、そのフォルダやファイル名を返し、無い場合は新規に作成してくれます。

CreateFileAsyncメソッドで現在の年月日時間分秒.pngファイルを作成します。メンバ変数saveImageFileNameに、このpngファイル名を格納しておきます。

イメージストリームの書式を表す、新しいImageEncodingPropertiesクラスのインスタンス、myImageEncodingPropertyオブジェクトを作成します。書式のサブタイプを表すSubtypeプロパティにpngを指定し、Widthに1024、Heightに768と指定します。

CapturePhotoToStorageFileAsyncメソッドで、ストレージファイルにフォトをキャプチャします。書式は以下の通りです。

CapturePhotoToStorageFileAsync(ImageEncodingProperties,IStorageFile)

マウスの右クリックで表示される「Folder」アイコンの使用を可能にします。「保存しました。」のメッセージを表示します。

非同期処理で行われるためメソッドの先頭にAsyncを追加します。

  Private Async Sub cameraButton_Click(sender As Object, e As RoutedEventArgs) Handles cameraButton.Click
    MediaElement1.Play()
    Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
    Dim mySubFolder = Await myFolder.CreateFolderAsync("myPictureAlbum", CreationCollisionOption.OpenIfExists)
    Dim myFile As StorageFile = Await mySubFolder.CreateFileAsync(DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") & ".png")
    saveImageFileName = DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") & ".png"
    Dim myImageEncodingProperty As New ImageEncodingProperties
    myImageEncodingProperty.Subtype = "png"
    myImageEncodingProperty.Width = 1024
    myImageEncodingProperty.Height = 768
 
      Await myMediaCapture.CapturePhotoToStorageFileAsync(myImageEncodingProperty, myFile)
    chiranButton.IsEnabled = True
    Dim message As New MessageDialog("保存しました。")
    Await message.ShowAsync
  End Sub

戻る(←)アイコンがタップされた時の処理

マウスの右クリックで表示される「Attach Camera」アイコンの使用を可能にします。
myFrameを非表示とします。
Webカメラのデバイス名の表示されているComboBox1を表示状態にします。

  Private Sub backButton_Click(sender As Object, e As RoutedEventArgs) Handles backButton.Click
    cameraButton.IsEnabled = True
    myFrame.Visibility = Xaml.Visibility.Collapsed
    ComboBox1.Visibility = Xaml.Visibility.Visible
  End Sub

マウスの右クリックで表示される「Folder」アイコンがタップされた時の処理

「Attach Camera」アイコンの使用を不可とします。Webカメラのデバイス名の表示されているComboBox1を非表示にします。

myFrameを表示状態にし、NavigateメソッドでDataIchiranShowPageに遷移します。値は渡さないのでNothingとしておきます。

  Private Sub ichiranButton_Click(sender As Object, e As RoutedEventArgs) Handles ichiranButton.Click
    cameraButton.IsEnabled = False
    ComboBox1.Visibility = Xaml.Visibility.Collapsed
    myFrame.Visibility = Xaml.Visibility.Visible
    myFrame.Navigate(GetType(DataIchiranShowPage), Nothing)
  End Sub

マウスの右クリックで表示される「Delete」アイコンをタップした時の処理

「Attach Camera」アイコンの使用を不可とします。
Webカメラのデバイス名の表示されているComboBox1を非表示にします。

myFrameを表示状態にし、NavigateメソッドでDeletePageに遷移します。値は渡さないのでNothingとしておきます。

  Private Sub deleteShowButton_Click(sender As Object, e As RoutedEventArgs) Handles deleteShowButton.Click
    cameraButton.IsEnabled = False
    ComboBox1.Visibility = Xaml.Visibility.Collapsed
    myFrame.Visibility = Xaml.Visibility.Visible
    myFrame.Navigate(GetType(DeletePage), Nothing)
  End Sub
End Class

次に、ソリューションエクスプローラー内のDataIchiranShowPage.xamlを展開して表示される、DataIchiranShowPage.xaml.vbをダブルクリックして、リスト5のコードを記述します。

ロジックコードを記述する

リスト5 (DataIchiranShowPage.xaml.vb)

Option Strict On

Imports Windows.Storage
Imports Windows.UI.Popups

ImageInfoクラス内にBitmapImage型の「画像名」プロパティを定義しておきます。

Public Class ImageInfo
  Public Property 画像名 As BitmapImage
End Class

Public NotInheritable Class DataIchiranShowPage
  Inherits Page

インデックスによってアクセスできる要素の読み取り専用コレクションを表す、IstorageFile型のメンバ変数myPictureFilesを宣言します。

  Dim myPictureFiles As IReadOnlyList(Of IStorageFile)

ページがアクティブになった時の処理

画像のデータをFlipViewで表示するDataShowプロシージャを実行します。

  Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    DataShow()
  End Sub

画像のデータをFlipViewで表示する処理

ピクチャライブラリのサブフォルダmyPictureAlbumにアクセスします。
GetFilesAsyncメソッドで、myPictureAlbumフォルダ内のファイルを取得し、コレクションメンバ変数myPictureFilesに格納します。

ImageInfoクラス型の新しいリストである、myImageInfoオブジェクトを作成します。Countプロパティでコレクションメンバ変数myPictureFilesが格納しているファイルの個数を取得して、ファイルが無い場合は警告メッセージを表示して処理を抜けます。

ファイルがある場合は以下の処理を行います。
コレクションメンバ変数myPictureFiles内を変数myFileで反復しながら、以下の処理を行います。
新しいBitmapImageのインスタンスbmpオブジェクトを作成します。SetSourceメソッドに、
Await myFile.OpenReadAsync
と指定して、OpenReadAsyncメソッドで、ランダムアクセスストリームを開き、BitmapSourceのソースイメージに設定します。
ImageInfoクラスの「画像名」プロパティにbmpオブジェクトを指定して、AddメソッドでmyImageInfoオブジェクトに追加していきます。FlipViewのItemsSourceプロパティにmyImageInfoオブジェクトを追加します。これで、FlipViewにmyPictureAlbumフォルダ内の画像が表示されます。

FlipViewのSelectedIndexにmyPictureFilesコレクション変数内の、最後の画像(現在撮られた画像)を指定します。-1しているのは、SelectedIndexの値は0から始まるためです。
countTextBlock内に何枚目の何番目の画像かを、1/10~10/10の形式で表示します。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

  Private Async Sub DataShow()
    Dim pictureFolder As StorageFolder = KnownFolders.PicturesLibrary
    Dim pictureSubFolder As StorageFolder = Await pictureFolder.CreateFolderAsync("myPictureAlbum", CreationCollisionOption.OpenIfExists)
    myPictureFiles = Await pictureSubFolder.GetFilesAsync()
    Dim myImageInfo As New List(Of ImageInfo)
    If myPictureFiles.Count = 0 Then
      Dim message As New MessageDialog("表示するデータはありません。戻ってください。")
      Await message.ShowAsync
      FlipView1.ItemsSource = Nothing
      Exit Sub
    Else
      For Each myFile As IStorageFile In myPictureFiles
        Dim bmp As New BitmapImage
        bmp.SetSource(Await myFile.OpenReadAsync)
        myImageInfo.Add(New ImageInfo With {.画像名 = bmp})
    Next
    FlipView1.ItemsSource = myImageInfo
    FlipView1.SelectedIndex = myPictureFiles.Count - 1
    countTextBlock.Text = FlipView1.SelectedIndex + 1 & "/" & myPictureFiles.Count
  End If
End Sub

FlipViewの画像が変更された時の処理

countTextBlock内に何枚目の何番目の画像かを、1/10~10/10の形式で表示します。

  Private Sub FlipView1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles FlipView1.SelectionChanged
    countTextBlock.Text = FlipView1.SelectedIndex + 1 & "/" & myPictureFiles.Count
  End Sub
End Class

次に、ソリューションエクスプローラー内のDeletePage.xamlを展開して表示される、DeletePage.xaml.vbをダブルクリックして、リスト5のコードを記述します。

ロジックコードを記述する

リスト5 (DeletePage.xaml.vb)

Option Strict On

Imports Windows.Storage
Imports Windows.UI.Popups

DeleteInfoクラスに、BitmapImage型の「削除画像名」と、文字列型の「ファイル名」プロパティを定義しておきます。

Public Class DeleteInfo
  Public Property 削除画像名 As BitmapImage
  Public Property ファイル名 As String
End Class
Public NotInheritable Class DeletePage
  Inherits Page

インデックスによってアクセスできる要素の読み取り専用コレクションを表す、IstorageFile型のメンバ変数myPictureFilesを宣言します。

  Dim myPictureFiles As IReadOnlyList(Of IStorageFile)

ページがアクティブになった時の処理

削除ボタンの追加された小さな画像を一覧で表示する、DataShowプロシージャを実行します。

  Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    DataShow()
  End Sub

削除ボタンの追加された小さな画像を一覧で表示する処理

ピクチャライブラリ内のサブフォルダmyPictureAlbumにアクセスします。GetFilesAsyncメソッドで、myPictureAlbumフォルダ内のファイルを取得し、コレクションメンバ変数myPictureFilesに格納します。
Countプロパティでコレクションメンバ変数myPictureFilesが格納しているファイルの個数を取得して、ファイルが無い場合は警告メッセージを表示して処理を抜けます。

ファイルがある場合は以下の処理を行います。

コレクションメンバ変数myPictureFiles内を変数myFileで反復しながら、以下の処理を行います。
新しいBitmapImageのインスタンスbmpオブジェクトを作成します。SetSourceメソッドに、
Await myFile.OpenReadAsync
と指定して、OpenReadAsyncメソッドで、ランダムアクセスストリームを開き、BitmapSourceのソースイメージに設定します。

DeleteInfoクラスの「削除画像名」プロパティにbmpオブジェクトを、「ファイル名」プロパティにPathプロパティで、完全なファイルシステムパスを取得し、Path.GetFileNameWithoutExtensionメソッドで、パスと拡張子を除いたファイル名を取得して、指定し、AddメソッドでmyDeleteInfoオブジェクトに追加していきます。
GridViewのItemsSourceプロパティにmyDeleteInfoオブジェクトを追加します。GridView内に「削除」ボタンの付けいた画像の一覧が表示されます。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

  Private Async Sub DataShow()
    Dim pictureFolder As StorageFolder = KnownFolders.PicturesLibrary
    Dim pictureSubFolder As StorageFolder = Await pictureFolder.CreateFolderAsync("myPictureAlbum", CreationCollisionOption.OpenIfExists)
    myPictureFiles = Await pictureSubFolder.GetFilesAsync()
    If myPictureFiles.Count = 0 Then
      Dim message As New MessageDialog("データがありません。戻ってください。")
      Await message.ShowAsync
      GridView1.ItemsSource = Nothing
      Exit Sub
    Else
      Dim myDeleteInfo As New List(Of DeleteInfo)
      For Each myFile As IStorageFile In myPictureFiles
        Dim bmp As New BitmapImage
        bmp.SetSource(Await myFile.OpenReadAsync)
        myDeleteInfo.Add(New DeleteInfo With {.削除画像名 = bmp, .ファイル名 = Path.GetFileNameWithoutExtension(myFile.Path)})
    Next
    GridView1.ItemsSource = myDeleteInfo
  End If
End Sub

「削除」ボタンがクリックされた時の処理

画像が選択状態ではない場合は、警告メッセージを表示して処理を抜けます。

画像が選択されて「削除」ボタンがクリックされた場合は、以下の処理を行います。
コレクションメンバ変数myPictureFiles内の、GridViewのSelectedIndexに該当するファイルを、DeleteAsyncメソッドで削除します。
「削除しました。」のメッセージを表示し、GridView内を再描画するために、DataShowプロシージャを実行します。

非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

  Private Async Sub DeleteGo()
    If GridView1.SelectedIndex = -1 Then
      Dim message As New MessageDialog("削除する画像を選択してください。")
      Await message.ShowAsync
      Exit Sub
    Else
      Await myPictureFiles(GridView1.SelectedIndex).DeleteAsync
      Dim message As New MessageDialog("削除しました。")
      Await message.ShowAsync
      DataShow()
    End If
  End Sub
End Class

アイコンの作成

詳細については、「自分の現在位置を取得して表示するサンプルプログラム」の記事を参照してください。

今回はここまでです。ありがとうございました。

  • マイフォトのサンプルプログラム

薬師寺国安事務所

薬師寺国安事務所代表。Visual Basic プログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。1997年に薬師寺聖とコラボレーション・ユニット PROJECT KySS を結成。2003年よりフリーになり、PROJECT KySS の活動に本格的に参加、.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Windows Phoneアプリ開発を経て、現在はWindows ストア アプリを多数公開中

Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。Microsoft MVP for Development Platforms-Windows Platform Development (Oct 2014-Sep 2015)。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています