カメラに配置した装飾アイテムを移動・変形させるサンプル
次に、CatchPhotoPage.xamlを展開して表示されるCatchPhotoPage.xaml.vbをダブルクリックして、リスト5のコードを記述します。
ロジックコードを記述する
リスト5 (CatchPhotoPage.xaml.vb)
Option Strict On Imports Microsoft.Devices Imports System.IO Imports System.IO.IsolatedStorage Imports System.Xml.Linq Imports Microsoft.Phone Imports System.Windows.Media.Imaging
画像の上をピンチして拡大縮小、回転、ドラッグを可能にするクラスの含まれる、MultiTouch.Behaviors.Silverlight4名前空間をインポートします。
Imports MultiTouch.Behaviors.Silverlight4 Imports System.Windows.Interactivity Partial Public Class CatchPhotoPage Inherits PhoneApplicationPage Public Sub New() InitializeComponent() End Sub
myPhotoCameraを、PhotoCameraクラスのメンバ変数として宣言します。PhotoCameraクラスは、カメラアプリケーションの基本カメラ機能を提供し、イメージ キャプチャ、フォーカス、解像度、フラッシュ モードなどの機能を有効にして構成するためのメンバが含まれています。
Dim myPhotoCamera As PhotoCamera Dim imageFileName As String Dim recordDate As String Dim bufferValue As Integer = 5119
画像の上をピンチして拡大縮小、回転、ドラッグを可能にするMultiTouchBehaviorの新しいインスタンス、behaviorオブジェクトを作成します。
Dim behavior As New MultiTouchBehavior
Imageクラスの新しいインスタンスmyImageオブジェクトを作成します。
Dim myImage As New Image
ページがアクティブになった時呼び出されるメソッド
NavigationModeがNavigationMode.Backで、「戻る」 ナビゲーション履歴の最新コンテンツへの移動がなされた時は、myImageオブジェクトを非表示にします。それ以外は表示します。これはBackキーでこのページに戻ってきた時は、配置していた四文字熟語の画像を非表示にする処理です。
MainPage.xamlから送られた引数の値をNavigationContext.QueryStringで受け取り、変数kanjiNameに格納しておきます。四文字熟語の画像ファイル名が格納されます。ImageのインスタンスmyImageオブジェクトのプロパティを設定します。SourceプロパティにはkanjiName変数の値を指定します。InkPresenterにmyImageオブジェクトが追加されていない場合は、追加します。
MultiTouchBehaviorのインスタンス、behaviorの各プロパティを設定します。回転、拡大縮小、ドラッグに関するプロパティにTrueを指定します。AreFingersVisibleにTrueを指定すると指でホールドした位置に●の円が表示されます。
このプロパティに関する詳細な情報はWebにも掲載されていませんので、このように設定するものだと認識してください。Attachメソッドで指定されたオブジェクトにアタッチします。この場合、myImageオブジェクトにアタッチされ、配置された画像は、どの場所にでも回転、拡大縮小、ドラッグが可能になります。
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineで分離ストレージ内のMultiTouchBehaviorというフォルダとImageList.xmlファイルを連結し、変数filePathに格納します。
分離ストレージのMultiTouchBehaviorフォルダにImageList.xmlファイルが存在する場合は、「フォルダ」アイコンの「データ一覧」ボタンを使用可能にし、それ以外は使用不可とします。PhotoCameraの新しいインスタンス、myPhotoCameraオブジェクトを作成します。Rectangleに配置したmyVideoBrushという名前のVideoBrushに、SetSourceメソッドでmyPhotoCameraオブジェクトを指定します。
AddHandlerステートメントで、カメラ オブジェクトが初期化された時に発生する、Initializedイベントにイベントハンドラを追加します。Succeededプロパティで、カメラ操作が失敗した時は処理を抜けます。
それ以外は、変数myResolution を使って、AvailableResolutionsプロパティで、使用できるカメラの解像度を参照します。カメラでキャプチャしたイメージの解像度を設定できるResolutionプロパティに、先に取得したmyResolutionの一番先頭の解像度(640×480)を指定します。フラッシュモードを自動に設定します。
Try~Catch~End Tryで例外処理を行います。例外が発生した場合は、メッセージを表示します。別スレッドからの表示になります。
AddHandlerステートメントで、イメージが利用可能な場合に発生するCaptureImageAvailableイベントに、myPhotoCamera_CaptureImageAvailableイベントハンドラを追加します。
Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs) If e.NavigationMode = NavigationMode.Back Then myImage.Visibility = Windows.Visibility.Collapsed Else myImage.Visibility = Windows.Visibility.Visible End If Dim kanjiName As String = NavigationContext.QueryString("imagename") With myImage .Width = 400 .Height = 100 .Stretch = Stretch.Uniform .Source = New BitmapImage(New Uri(kanjiName, UriKind.Relative)) End With
'この記述がないとBackキーで戻った時Element is already the child of another element.のエラーが出るので注意
If InkPresenter1.Children.Contains(myImage) = False Then InkPresenter1.Children.Add(myImage) End If With behavior .CenterX = 200 .CenterY = 50 .IsInertiaEnabled = False .IsTranslateEnabled = True .IsScaleEnabled = True .MinimumScale = 50 .MaximumScale = 360 .IsPivotEnabled = False .AreFingersVisible = True .Attach(myImage) End With Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim filePath As String = Path.Combine("MultiTouchBehavior", "ImageList.xml") If storage.FileExists(filePath) = True Then TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = True Else TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = False End If myPhotoCamera = New PhotoCamera myVideoBrush.SetSource(myPhotoCamera) AddHandler myPhotoCamera.Initialized, Sub(cameraSender As Object, cameraArgs As CameraOperationCompletedEventArgs) Try If cameraArgs.Succeeded = False Then Exit Sub Else Dim myResolution As IEnumerable(Of Size) = myPhotoCamera.AvailableResolutions myPhotoCamera.Resolution = myResolution.First myPhotoCamera.FlashMode = FlashMode.Auto End If Catch Deployment.Current.Dispatcher.BeginInvoke(Sub() MessageBox.Show("カメラが開始するまでしばらくお待ちください。") End Sub) End Try End Sub AddHandler myPhotoCamera.CaptureImageAvailable, AddressOf myPhotoCamera_CaptureImageAvailable MyBase.OnNavigatedTo(e) End Sub
イメージが利用可能な場合に発生する処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineで、MultiTouchBehaviorというフォルダとimageFileNameに格納されている画像名とを連結し、変数filePathに格納します。同様にMultiTouchBehaviorというフォルダとImageList.xmlというファイル名を連結して、変数xmlFilePathに格納します。
IsolatedStorageFileクラスのCreateFileメソッドで変数filePathに格納されているファイルを作成します。バイト型として5119の領域を確保します。e.ImageStream.Readメソッドで、イメージストリームからバイト数を読み取り、変数myByteReadに格納します。この変数が、0より大きい間、IsolatedStorageFileStream.Writeメソッドで、読み込んだイメージストリームのバイト数をバッファーに書き込んでいきます。
写真の撮影とエンコードが正しくできているかは、ContentReadyEventArgs.ImageStreamプロパティから取得できますので、これを分離ストレージへ保存します。e.ImageStreamを閉じます。
e.ImageStream.Readの書式は下記の通りです。
e.ImageStream.Read(バイト配列, データの格納を開始するバッファー内のバイト オフセット(通常0を指定), 現在のストリームから読み取るバイトの最大数。)
IsolatedStorageFileStream.Writeメソッドの書式は下記の通りです。
IsolatedStorageFileStream.Write(書き込むバッファー, 開始位置を示すバッファー内のバイト オフセット(通常0を指定) ,書き込む最大バイト数,)
別スレッドで以下の処理を行います。
変数imageStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数stream変数を用意し、IsolatedStorageFile.OpenFileメソッドで、filePathに格納しているファイルを、指定したモード、指定したファイルアクセスモードで開きます。
WriteableBitmap型の変数imageSourceを宣言し、PictureDecoder.DecodeJpeg(stream)で、撮った写真をJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。PictureDecoder.DecodeJpegメソッドはMicrosof.Phone名前空間に属しています。
backgroundImageのSourceプロパティにimageSourceオブジェクトを指定します。一度分離ストレージに保存した画像を読み込んで、backgroundImageに表示しています。
InkPresenterで初期化された新しいWriteableBitmapのインスタンスを作成します。backgroundImageに画像を表示させた時点で、分離ストレージのMultiTouchBehaviorフォルダ内の保存した画像を、DeleteFileメソッドで削除しておきます。
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数Stream変数を用意し、IsolatedStorageFile.CreateFileメソッドで分離ストレージのMultiTouchBehaviorフォルダ内に、変数imageFileNameに格納されている画像ファイルを作成します。
Extensions.SaveJpegメソッドで、WriteableBitmapオブジェクトを、JPEGストリームにエンコードします。これは、JPEGファイルのターゲットとなる幅と高さを設定するためのパラメータを持っています。書式は下記の通りです。
Extensions.SaveJpeg(WriteableBitmapオブジェクト,イメージデータストリーム,WriteableBitmapオブジェクトのPixelWidth, WriteableBitmapオブジェクトのPixelHeight,0(固定),0~100の間の写真の品質(70以上を指定))
分離ストレージのMultiTouchBehavior内に指定した画像ファイルが作成されます。
変数xmlStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。MultiTouchBehaviorというフォルダ内にImageList.xmlが存在していない場合は、Visual Basic の埋め込み式を用いて、XML宣言とルート要素
埋め込み式の構文である を用いて“RecordDate”属性の値にメンバ変数recordDateの値を、
分離ストレージ内のファイルを表す、IsolatedStorageFileStreamクラス用のオブジェクト変数であるxmlStreamを用意し、IsolatedStorageFile.CreateFileメソッドで、分離ストレージ内にxmlFilePath変数の持っているフォルダ名付きXMLファイルを作成します。
ImageList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。
Writeメソッドで埋め込み式のXMLをストリームに書き込みます。別スレッドで、保存した旨のメッセージを表示し、「データ一覧」のフォルダアイコンの使用を可能にするIchiranShowプロシージャを実行します。
次は、既に最初からMultiTouchBehaviorフォルダ内に、ImageList.xmlが存在する場合の処理です。
IsolatedStorageFileクラスのOpenFileメソッドで、MultiTouchBehaviorフォルダ内のImageList.xmlファイルを、指定したファイルアクセスを使用して指定したモードで開きます。開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り変数readXmldoc変数に格納しておきます。読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。
追加する
ImageList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開きます。Writeメソッドで新しい要素の追加されたXMLを、ストリームに書き込みます。別スレッドで、保存した旨のメッセージを表示し、「データ一覧」のフォルダアイコンの使用を可能にするIchiranShowプロシージャを実行します。
Private Sub myPhotoCamera_CaptureImageAvailable(sender As Object, e As ContentReadyEventArgs) Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim filePath As String = Path.Combine("MultiTouchBehavior", imageFileName) Dim xmlFilePath As String = Path.Combine("MultiTouchBehavior", "ImageList.xml") Using myStream As IsolatedStorageFileStream = storage.CreateFile(filePath) Dim myBuffer(bufferValue) As Byte Dim myByteRead As Integer = -1 Do myByteRead = e.ImageStream.Read(myBuffer, 0, myBuffer.Length) myStream.Write(myBuffer, 0, myByteRead) Loop While (myByteRead > 0) e.ImageStream.Close() End Using Deployment.Current.Dispatcher.BeginInvoke(Sub() Dim imageStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Using stream As IsolatedStorageFileStream = imageStorage.OpenFile(filePath, FileMode.Open, FileAccess.Read) Dim imageSource As WriteableBitmap = PictureDecoder.DecodeJpeg(stream) backgroundImage.Source = imageSource End Using Dim myWriteableBitmap As WriteableBitmap myWriteableBitmap = New WriteableBitmap(InkPresenter1, Nothing) If imageStorage.FileExists(Path.Combine("MultiTouchBehavior", imageFileName)) = True Then imageStorage.DeleteFile(Path.Combine("MultiTouchBehavior", imageFileName)) End If Using Stream As IsolatedStorageFileStream = storage.CreateFile(Path.Combine("MultiTouchBehavior", imageFileName)) System.Windows.Media.Imaging.Extensions.SaveJpeg(myWriteableBitmap, Stream, 480, 640, 0, 85) End Using End Sub) 'XMLファイルの保存 Dim xmlStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication If xmlStorage.FileExists(xmlFilePath) = False Then Dim xmldoc As XDocument = <?xml version="1.0" encoding="utf-8"?> <Kanji> <Pictures RecordDate=<%= recordDate %>> <PictureName><%= imageFileName %></PictureName> </Pictures> </Kanji> Using xmlStream As IsolatedStorageFileStream = xmlStorage.CreateFile(xmlFilePath) End Using If xmlStorage.FileExists(xmlFilePath) = True Then Using xmlwriter As StreamWriter = New StreamWriter(xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Write), System.Text.Encoding.UTF8) xmlwriter.Flush() xmlwriter.Write(xmldoc.ToString) End Using Deployment.Current.Dispatcher.BeginInvoke(Sub() backgroundImage.Source = Nothing MessageBox.Show("画像とXMLファイルを保存しました。") IchiranShow() End Sub) End If Else Dim xmlStream As IsolatedStorageFileStream = xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Read) Using xmlreader As StreamReader = New StreamReader(xmlStream) Dim readXmldoc As String = xmlreader.ReadToEnd Dim doc As XElement = XElement.Parse(readXmldoc) Dim addXml As XElement = <Pictures RecordDate=<%= recordDate %>> <PictureName><%= imageFileName %></PictureName> </Pictures> doc.Add(addXml) xmlStream.Close() If xmlStorage.FileExists(xmlFilePath) = True Then Using xmlwriter As StreamWriter = New StreamWriter(storage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Write), System.Text.Encoding.UTF8) xmlwriter.Flush() xmlwriter.Write(doc.ToString) Deployment.Current.Dispatcher.BeginInvoke(Sub() backgroundImage.Source = Nothing MessageBox.Show("画像とXMLファイルを保存しました。") IchiranShow() End Sub) End Using End If End Using End If End Sub
[シャッター]アイコンがタップされた時の処理
PhotoCameraにファインダーに表示されるイメージがある場合は、現在の年月日時間分秒.jpgファイル名を作成し、メンバ変数imageFileNameに格納しておきます。また、現在の何年/何月/何日/何時:何分:何秒という文字列を、メンバ変数recordDateに格納しておきます。
CaptureImageメソッドで、ファインダーに表示される現在のイメージのフル解像度キャプチャを開始します。例外が発生した場合は別スレッドで、メッセージを表示します。
Private Sub GoShutter(sender As Object, e As EventArgs) If myPhotoCamera Is Nothing = False Then Try imageFileName = DateTime.Now.ToString("yyyyMMddHHmmss") & ".jpg" recordDate = DateTime.Now.ToString("yyyy/MM/dd/HH:mm:ss") myPhotoCamera.CaptureImage() Catch Me.Dispatcher.BeginInvoke(Sub() MessageBox.Show("エラーが発生しましたが、データは保存されます。")) End Try End If End Sub
[データ一覧]のアイコンがタップされた時の処理
InkPresenterに配置している四文字熟語の画像を削除し、これから作成する、DataIchiranPage.xamlに遷移します。
Private Sub GoIchiran(sender As Object, e As EventArgs) InkPresenter1.Children.Remove(myImage) NavigationService.Navigate(New Uri("/DataIchiranPage.xaml", UriKind.Relative)) End Sub
[四文字熟語選択]のアイコンがタップされた時の処理
MainPage.xamlに遷移します。
Private Sub KanjiImageSelect(sender As Object, e As EventArgs) NavigationService.Navigate(New Uri("/MainPage.xaml", UriKind.Relative)) End Sub
[データ一覧]アイコンの使用可否を決める処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineメソッドで分離ストレージ内のMultiTouchBehaviorというフォルダとImageLis.xmlというファイル名を連結して、変数filePathに格納します。filePathに格納しているファイルが存在する場合は、[データ一覧]アイコンの使用を可能にし、それ以外は使用不可にします。
Private Sub IchiranShow() Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim filePath As String = Path.Combine("MultiTouchBehavior", "ImageList.xml") If storage.FileExists(filePath) = True Then TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = True Else TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = False End If End Sub
[熟語画像の削除]アイコンがタップされた時の処理
InkPresenterに配置されている四文字熟語の画像を消去します。
Private Sub GoDelete(sender As Object, e As EventArgs) InkPresenter1.Children.Remove(myImage) End Sub End Class
「Windows Phone 縦向きのページ」(DataIchiranPage.xaml)の作成
VS2010メニューの「プロジェクト(P)/新しい項目の追加(W)」と選択し、「Windows Phone 縦向きのページ」を選択します。「名前(N)」にはDataIchiranPage.xamlと入力します。
(*)コードとコード解説については、以前の記事「写真をハート型に切り抜いて撮影するサンプル」のDataIchiranPage.xamlとDataIchiranPage.xaml.vbとほとんど同じ処理になりますので、同記事のDataIchiranPage.xaml.vbの解説を参照してください。
今回のサンプルは以上で終了です。
【参照リンク】
PROJECT KySSでは現在、16個のWindows PhoneアプリをMarketplaceに公開しています。試用版もありますので、興味のある方はお試しください。
→参照:Windows Phone App Information(PROJECT KySS)
カメラに配置した装飾アイテムを移動・変形させるサンプル