写真をハート型に切り抜いて撮影するサンプル
次に、DataIchiranPage.xamlを展開して表示される、DataIchiranPage.xaml.vbをダブルクリックしてリスト4のコードを記述します。
ロジックコードを記述する
リスト4 (DataIchiranPage.xaml.vb)
Option Strict On Imports System.Xml.Linq Imports System.IO Imports System.IO.IsolatedStorage Imports System.Windows.Media.Imaging Imports Microsoft.Phone
ImageInfoクラス内に、WriteableBitmapクラス型のimageFileNameと、文字列型のrecordDateプロパティを定義しておきます。
Public Class ImageInfo Property imageFileName As WriteableBitmap Property recordDate As String End Class Partial Public Class DataIchiranPage Inherits PhoneApplicationPage Public Sub New() InitializeComponent() End Sub
新しいMenuItemクラスのインスタンスmyMenuItem1~myMenuItem2をメンバ変数として宣言します。MenuItemクラスは、ContextMenu 内に表示される個別の項目を表すクラスです。
Dim myMenuItem1 As New MenuItem Dim myMenuItem2 As New MenuItem
ContextMenuクラスの新しいインスタンスmyContextMenuオブジェクトをメンバ変数として宣言します。ContextMenuコントロールは、コントロールのコンテキストに固有の機能を公開するポップアップメニューを表示するコントロールです。このコントロールは、Silverlight for Windows Phone Toolkit - Nov 2011.msiに含まれていますので、下記URLよりダウンロードしてインストールしてください。
→参照:Windows Phone Toolkit - Nov 2011 (7.1 SDK)
Dim myContextMenu As New ContextMenu Dim imageName As String Dim doc As XElement Dim myIndex As Integer
ページがアクティブになった時の処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.CombineメソッドでImageInFrameフォルダとImageList.xmlのファイル名を連結して、filePath変数に格納します。
IsolatedStorageFileクラスのOpenFileメソッドで、ImageInFrameフォルダ内のImageList.xmlファイルを、指定したファイルアクセスを使用して指定したモードで開き、StreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り、変数readXmldocに格納しておきます。読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。
ImageInfoクラスの新しいリストであるmyImageInfoを作成します。
Descendantsメソッドで、子孫要素であるすべての 要素のコレクションを選択し、各要素を、変数resultに格納しながら、以下の処理を繰り返します。
Path.Combineメソッドで、ImageInFrameフォルダと要素の内容を連結して、変数imageFilePathに格納しておきます。
変数imageStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数stream変数を用意し、IsolatedStorageFile.OpenFileメソッドでimageFilePathに格納しているファイルを、指定したモード、指定したファイルアクセスモードで開きます。
WriteableBitmap型の変数imageSourceを宣言し、PictureDecoder.DecodeJpegメソッドで、開いたストリームをJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。PictureDecoder.DecodeJpegメソッドはMicrosof.Phone名前空間に属しています。WriteableBitmapクラスは書き込み更新することのできるBitmapSourceを提供するクラスです。
ImageInfoクラスのimageFileNameプロパティに、読み込んだWriteableBitmapオブジェクトのimageSourceオブジェクトを指定し、recordDateプロパティに要素の属性”記録日”の値を指定して、AddメソッドでmyImageInfoオブジェクトに追加していきます。ListBox1のItemsSourceプロパティにmyImageInfoオブジェクトを指定します。
これで、撮った画像と記録日の一覧が表示されます。
Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs) Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim filePath As String = Path.Combine("ImageInFrame", "ImageList.xml") Using myStream As IsolatedStorageFileStream = storage.OpenFile(filePath, FileMode.Open, FileAccess.Read) Using reader As StreamReader = New StreamReader(myStream) Dim readXmldoc As String = reader.ReadToEnd doc = XElement.Parse(readXmldoc) Dim myImageInfo As New List(Of ImageInfo) For Each result In From c In doc.Descendants("風景") Select c Dim imageFilePath As String = Path.Combine("ImageInFrame", result.Element("画像名").Value) Dim imageStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Using stream As IsolatedStorageFileStream = imageStorage.OpenFile(imageFilePath, FileMode.Open, FileAccess.Read) Dim imageSource As WriteableBitmap = PictureDecoder.DecodeJpeg(stream) With myImageInfo .Add(New ImageInfo With {.imageFileName = imageSource, .recordDate = result.Attribute("記録日").Value}) End With 'stream.Close() End Using Next ListBox1.ItemsSource = myImageInfo End Using End Using MyBase.OnNavigatedTo(e) End Sub
リストボックスから任意の画像が選択された時の処理
ListBoxより、選択されたインデックスをメンバ変数myIndexに格納しておきます。
myIndexに該当する要素の子要素の値を取得して、メンバ変数imageNameに格納しておきます。MenuItem型の新しいリストであるmenuItemListを作成します。
Header、FontSize、FontWeightプロパティを設定し、memuItemListオブジェクトに各プロパティの設定されたmenuItem1とmenuItem2を追加します。ContextMenuのItemsSourceにmenuItemListオブジェクトを指定し、透明度を表すOpacityに0.8を指定して、少し透明化させます。ContextMenuService.SetContextMenu メソッドでListBoxオブジェクトにmyContextMenuオブジェクトの値を設定します。IsOpenメソッドでコンテキストメニューを開きます。
AddHandlerステートメントでmyMenuItem1とmyMenuItem2のClickイベントにmyMenuItem_Clickのイベントハンドラを指定します。その前に、一度RemoveHandlerステートメントで削除のイベントである、myMenuItem1のClickイベントのイベントハンドラを削除しておきます。この処理を追加していないと、削除確認のメッセージが重複して表示される場合があります。
Private Sub ListBox1_SelectionChanged(sender As Object, e As System.Windows.Controls.SelectionChangedEventArgs) Handles ListBox1.SelectionChanged Try myIndex = ListBox1.SelectedIndex imageName = doc.Descendants("風景")(myIndex).Element("画像名").Value Dim menuItemList As New List(Of MenuItem) myMenuItem1.Header = "データの削除" myMenuItem1.FontSize = 25 myMenuItem1.FontWeight = FontWeights.Bold myMenuItem2.Header = "キャンセル" myMenuItem2.Foreground = New SolidColorBrush(Colors.Red) myMenuItem2.FontSize = 25 myMenuItem2.FontWeight = FontWeights.Bold menuItemList.Add(myMenuItem1) menuItemList.Add(myMenuItem2) myContextMenu.ItemsSource = menuItemList myContextMenu.Opacity = 0.8 ContextMenuService.SetContextMenu(ListBox1, myContextMenu) myContextMenu.UpdateLayout() myContextMenu.IsOpen = True RemoveHandler myMenuItem1.Click, AddressOf myMenuItem_Click AddHandler myMenuItem1.Click, AddressOf myMenuItem_Click AddHandler myMenuItem2.Click, AddressOf myMenuItem_Click Catch Exit Sub End Try End Sub
ContextMenuに表示されたメニューがタップされた時の処理
senderオブジェクトからMenuItemのHeaderプロパティの情報を取得して、変数selectHeaderに格納しておきます。selectHeaderの値で条件分岐を行います。
値が「データの削除」の場合は、削除確認メッセージを表示し、[ok]の場合は、DeleteDataプロシージャを実行します。[キャンセル]の場合は、ContexMenuを閉じ、ListBoxの選択を解除します。
Private Sub myMenuItem_Click(sender As Object, e As EventArgs) Try Dim selectHeader = DirectCast(sender, MenuItem).Header Select Case selectHeader.ToString Case "データの削除" Dim kakunin = MessageBox.Show("このデータを削除しますか?", "削除確認", MessageBoxButton.OKCancel) Select Case kakunin Case MessageBoxResult.OK DeleteData() Exit Select Case Else ListBox1.SelectedIndex = -1 Exit Sub End Select Case "キャンセル" myContextMenu.IsOpen = False ListBox1.SelectedIndex = -1 Exit Select Case Else ListBox1.SelectedIndex = -1 Exit Select End Select Catch Exit Sub End Try End Sub
データを削除する処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。分離ストレージ内のImageInFrameフォルダとImageList.xmlというファイルをPath.Combineメソッドで連結し、変数filePathに格納しておきます。ImageInFrameフォルダ内にImageList.xmlファイルが存在していた場合は、分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数myStreamを用意し、IsolatedStorageFile.OpenFileメソッドでfilePathに格納しているファイルを、指定したモード、指定したファイルアクセスモードで開きます。開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り変数readXmldocに格納しておきます。
読み込んだXMLテキストを、ParseメソッドでXElementとして読み込みます。myStreamオブジェクトを閉じます。
読み込んだXMLから、メンバ変数myIndexに該当する要素の子要素の値を取得し、変数delImageに格納しておきます。分離ストレージ内のImageInFrameフォルダにある、delImageに格納されている画像ファイル名をPath.Combineメソッドで連結して、変数delJPGfileに格納します。DeleteFileメソッドで、このファイルを削除します。
メンバ変数myIndexに該当する要素を選択します。Removeメソッドで、この要素とその子要素を全て削除します。
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数streamを用意し、filePath変数と、ファイルモード(作成モード)、ファイルアクセス(書き込み)で初期化された、新しいIsolatedStorageFileStreamを作成します。Saveメソッドで要素とその子要素の削除されたXMLを保存します。削除した旨を表示します。
要素を選択するクエリを定義します。要素が存在しない (全てのデータが削除された)場合は、ImageList.xml自体を削除し、MainPage.xamlに遷移します。まだImageList.xmlが存在している場合は、DataIchiranPage.xamlに遷移し、削除されたListBoxを再描画します。
Sub DeleteData() Dim delXml As XElement Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim filePath As String = Path.Combine("ImageInFrame", "ImageList.xml") If storage.FileExists(filePath) = True Then Dim myStream As IsolatedStorageFileStream = storage.OpenFile(filePath, FileMode.Open, FileAccess.Read) Using reader As StreamReader = New StreamReader(myStream, System.Text.Encoding.UTF8) Dim readXmldoc As String = reader.ReadToEnd delXml = XElement.Parse(readXmldoc) myStream.Close() End Using Dim delImage As String = delXml.Descendants("風景")(myIndex).Element("画像名").Value Dim delJPGfile As String = Path.Combine("ImageInFrame", delImage) If delImage <> String.Empty Then storage.DeleteFile(delJPGfile) End If Dim delElement = delXml.Descendants("風景")(myIndex) delElement.Remove() Using stream As IsolatedStorageFileStream = New IsolatedStorageFileStream(filePath, FileMode.Create, FileAccess.Write, storage) delXml.Save(stream) End Using MessageBox.Show("データを削除しました。") Dim allDeleteQuery = From c In delXml.Descendants("風景") Select c If allDeleteQuery.Count <= 0 Then storage.DeleteFile(filePath) storage.Dispose() NavigationService.Navigate(New Uri(String.Format("/MainPage.xaml?date={0}", DateTime.Now.ToShortDateString & DateTime.Now.ToLongTimeString), UriKind.Relative)) Exit Sub End If storage.Dispose() NavigationService.Navigate(New Uri(String.Format("/DataIchiranPage.xaml?date={0}", DateTime.Now.ToShortDateString & DateTime.Now.ToLongTimeString), UriKind.Relative)) Else storage.Dispose() Exit Sub End If End Sub
Backボタン(←)がタップされた時の処理
NavigationService.BackStack.Countで画面遷移の履歴スタック数を取得し、変数myStacCountに格納しておきます。
myStacCount -2と記述し、NavigationService.RemoveBackEntry()で、MainPageのみを残して遷移スタックを削除します。myStacCount -1とすると、アプリケーションが終了してしまうので注意してください。
Protected Overrides Sub OnBackKeyPress(e As System.ComponentModel.CancelEventArgs) Dim myStacCount = NavigationService.BackStack.Count For i As Integer = 0 To myStacCount - 2 NavigationService.RemoveBackEntry() Next MyBase.OnBackKeyPress(e) End Sub End Class
今回のサンプルは以上で終了です。
【参照リンク】
PROJECT KySSでは現在、16個のWindows PhoneアプリをMarketplaceに公開しています。試用版もありますので、興味のある方はお試しください。
→参照:Windows Phone App Information(PROJECT KySS)
フレームで切り抜いて撮影するサンプル