カメラ撮影時に装飾アイテムを追加するサンプル

次に、MainPage.xamlを展開して表示されるMainPage.xaml.vbをダブルクリックして、リスト3のコードを記述します。
ロジックコードを記述する
リスト3 (MainPage.xaml.vb)
Option Strict On Imports System.Windows.Media.Imaging Imports Microsoft.Phone
仮想ファイルシステムを作成および使用するための型が含まれている、System.IO.IsolatedStorage名前空間をインポートします。分離ストレージによって、安全なクライアント側のストレージが提供されます。
Imports System.IO.IsolatedStorage Imports System.IO Imports System.Xml.Linq Imports Microsoft.Devices
このサンプルでは、アタッチされた要素が、マウスのドラッグのジェスチャに応答して、要素の上に再配置するMouseDragElementBehaviorクラスを使用するため、このクラスの含まれる、Microsoft.Expression.Interactivity.Layout名前空間のインポートが必要です。
Imports Microsoft.Expression.Interactivity.Layout<br /> Imports System.Windows.Interactivity
ImageDataInfoというクラス内にimageというプロパティを定義しておきます。
Public Class ImageDataInfo
Property image As String
End Class
Partial Public Class MainPage
Inherits PhoneApplicationPage
' コンストラクター
Public Sub New()
InitializeComponent()
End Sub
Dim kanjiIndex As Integer
Dim imageByte As Byte()
myCameraをPhotoCameraクラスのメンバ変数として宣言します。PhotoCameraクラスは、カメラアプリケーションの基本カメラ機能を提供し、イメージ キャプチャ、フォーカス、解像度、フラッシュ モードなどの機能を有効にして構成するためのメンバが含まれています。
Dim myCamera As PhotoCamera Dim bufferValue As Integer = 5119 Dim imageFileName As String Dim recordDate As String Dim imageSource As WriteableBitmap Dim myImage As Image
Image型の新しいリストであるimageListメンバ変数を宣言します。
Dim imageList As New List(Of Image)
ImageInfoクラス型の新しいリストである、myImageInfoメンバ変数を宣言します。
Dim myImageInfo As New List(Of ImageDataInfo) Dim xmldoc As XElement Dim positionX As Double Dim positionY As Double Dim starPositionX As Double Dim starPositionY As Double Dim myAngle As Integer = 0 Dim starMyAngle As Integer = 0 Shared myIndex As Integer = 0
ページがアクティブになった時に呼び出されるメソッド
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。DirectoryExistsメソッドでAddingIconCameraというフォルダが存在しているかどうかをチェックし、存在していない場合は、CreateDirectoryメソッドでAddingIconCameraというフォルダを作成します。
Path.Combineで、AddingIconCameraというフォルダとImageList.xmlというファイルを連結し、変数xmlFilePathに格納します。ImageList.xmlファイルがAddingIconCameraフォルダに存在する場合は、「データ一覧」のフォルダアイコンの使用を可能にし、それ以外は使用不可としておきます。
PhotoCameraクラスの新しいインスタンスを作成し、myVideoBrushという名前のVideoBrushにSetSourceメソッドでmyCameraオブジェクトを指定します。
AddHandlerステートメントで、カメラ オブジェクトが初期化された時に発生する、Initializedイベントにイベントハンドラを追加します。Succeededプロパティで、カメラ操作が失敗した時は処理を抜けます。
それ以外は、フラッシュモードを自動に設定し、変数myResolution を使って、AvailableResolutionsプロパティで、使用できるカメラの解像度を参照します。カメラでキャプチャしたイメージの解像度を設定できる、Resolutionプロパティに、先に取得したmyResolutionの一番先頭の解像度を指定します。一番先頭の解像度は640×480の解像度です。
Try~Catch~End Tryで例外処理を行います。例外が発生した場合は、メッセージを表示します。別スレッドからの表示になります。
AddHandlerステートメントで、イメージが利用可能な場合に発生する、CaptureImageAvailableイベントに、myCamera_CaptureImageAvailableイベントハンドラを追加します。
Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs)
Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
If storage.DirectoryExists("AddingIconCamera") = False Then
storage.CreateDirectory("AddingIconCamera")
End If
Dim xmlFilePath As String = Path.Combine("AddingIconCamera", "ImageList.xml")
If storage.FileExists(xmlFilePath) = True Then
TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = True
Else
TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = False
End If
myCamera = New PhotoCamera
myVideoBrush.SetSource(myCamera)
AddHandler myCamera.Initialized, Sub(cameraSender As Object, cameraArgs As CameraOperationCompletedEventArgs)
Try
If cameraArgs.Succeeded = False Then
Exit Sub
Else
myCamera.FlashMode = FlashMode.Auto
Dim myResolution As IEnumerable(Of Size) = myCamera.AvailableResolutions
myCamera.Resolution = myResolution.First
End If
Catch
Deployment.Current.Dispatcher.BeginInvoke(Sub()
MessageBox.Show("カメラがスタートするまでお待ちください。")
End Sub)
End Try
End Sub
AddHandler myCamera.CaptureImageAvailable, AddressOf myCamera_CaptureImageAvailable
End Sub
イメージが利用可能な場合に発生する処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表す、IsolateStorageFileクラスとして宣言します。Path.Combineで、AddingIconCameraというフォルダとimageFileNameに格納されている画像名とを連結し、変数filePathに格納します。同様にAddingIconCameraというフォルダと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名前空間に属しています。WriteableBitmapクラスは書き込み更新することのできるBitmapSourceを提供するクラスです。dummyImageのSourceプロパティにimageSourceオブジェクトを指定します。一度分離ストレージに保存した画像を読み込んで、dummyImageに表示しています。
InkPresenterで初期化された、新しいWriteableBitmapのインスタンスを作成します。dummyImageに画像を表示させた時点で、分離ストレージのAddingIconCameraフォルダ内に保存した画像を、DeleteFileメソッドで削除しておきます。
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数Streamを用意し、IsolatedStorageFile.CreateFileメソッドで分離ストレージのAddingIconCameraフォルダ内に、変数imageFileNameに格納されている画像ファイルを作成します。
Extensions.SaveJpegメソッドで、WriteableBitmapオブジェクトを、JPEGストリームにエンコードします。これは、JPEGファイルのターゲットとなる幅と高さを設定するためのパラメータを持っています。書式は下記の通りです。
Extensions.SaveJpeg(WriteableBitmapオブジェクト,イメージデータストリーム,WriteableBitmapオブジェクトのPixelWidth, WriteableBitmapオブジェクトのPixelHeight,0(固定),0~100の間の写真の品質(70以上を指定))
分離ストレージのAddingIconCamera内に指定した画像ファイルが作成されます。
変数xmlStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。AddingIconCameraというフォルダ内にImageList.xmlが存在していない場合は、Visual Basic の埋め込み式を用いて、XML宣言とルート要素
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数xmlStreamを用意し、IsolatedStorageFile.CreateFileメソッドで、分離ストレージ内に、xmlFilePath変数の持っているフォルダ名付きXMLファイルを作成します。
ImageList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。Writeメソッドで埋め込み式のXMLをストリームに書き込みます。別スレッドで、保存した旨のメッセージを表示し、dummyImageを非表示にします。「データ一覧」アイコンの使用可、不可を決めるIchiranShowプロシージャを実行します。
次は、既にAddingIconCameraフォルダ内にImageList.xmlが存在する場合の処理です。
IsolatedStorageFileクラスのOpenFileメソッドでAddingIconCameraフォルダ内のImageList.xmlファイルを、指定したファイルアクセスを使用して、指定したモードで開きます。開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り、変数readXmldocに格納しておきます。読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。
追加する
ImageList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して、指定したモードでファイルを開きます。Writeメソッドで、新しいXML要素の追加されたXMLをストリームに書き込みます。別スレッドで、保存した旨のメッセージを表示し、dummyImageを非表示にします。「データ一覧」のアイコンの使用可、不可を決めるIchiranShowプロシージャを実行します。
Private Sub myCamera_CaptureImageAvailable(sender As Object, e As ContentReadyEventArgs)
Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
Dim filePath As String = Path.Combine("AddingIconCamera", imageFileName)
Dim xmlFilePath As String = Path.Combine("AddingIconCamera", "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)
dummyImage.Source = imageSource
End Using
Dim myWriteableBitmap As WriteableBitmap
myWriteableBitmap = New WriteableBitmap(InkPresenter1, Nothing)
If imageStorage.FileExists(Path.Combine("AddingIconCamera", imageFileName)) = True Then
imageStorage.DeleteFile(Path.Combine("AddingIconCamera", imageFileName))
End If
Using Stream As IsolatedStorageFileStream = storage.CreateFile(Path.Combine("AddingIconCamera", 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"?>
<PhotoBooth>
<Picture RecordDate=<%= recordDate %>>
<ImageName><%= imageFileName %></ImageName>
</Picture>
</PhotoBooth>
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()
MessageBox.Show("画像とXMLファイルを保存しました。")
dummyImage.Source = Nothing
dummyImage.Visibility = Windows.Visibility.Collapsed
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 = <Picture RecordDate=<%= recordDate %>>
<ImageName><%= imageFileName %></ImageName>
</Picture>
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()
dummyImage.Source = Nothing
dummyImage.Visibility = Windows.Visibility.Collapsed
MessageBox.Show("画像とXMLファイルを保存しました。")
IchiranShow()
End Sub)
End Using
End If
End Using
End If
End Sub
「データ一覧」のアイコンの使用可否を決める処理
分離ストレージ内のAddingIconCameraフォルダ内にImageList.xmlファイルが存在する場合は、「データ一覧」のアイコンの使用を可能にし、それ以外は使用不可とします。
Private Sub IchiranShow()
Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
Dim filePath As String = Path.Combine("AddingIconCamera", "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
ページが読み込まれた時の処理
XElement.Loadメソッドでkanji.xmlを読み込みます。Descendantsメソッドで、子孫要素であるすべての
ListBoxのItemsSourceプロパティにmyImageInfoオブジェクトを指定すると、漢字画像の一覧が表示されます。
Private Sub MainPage_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
xmldoc = XElement.Load("kanji.xml")
For Each result In From c In xmldoc.Descendants("image") Select c
myImageInfo.Add(New ImageDataInfo With {.image = "Image/" & result.Value})
Next
ListBox1.ItemsSource = myImageInfo
End Sub
「カメラ」アイコンをタップした時の処理
PhotoCameraにファインダーに表示されるイメージがある場合は、dummyImageコントロールを表示します。現在の年月日時間分秒.jpgファイル名を作成し、メンバ変数imageFileNameに格納しておきます。また、現在の年/月/日/時:分:秒という文字列を、メンバ変数recordDateに格納しておきます。
CaptureImageメソッドで、ファインダーに表示された現在のイメージのフル解像度キャプチャを開始します。例外が発生した場合は別スレッドで、メッセージを表示します。
Private Sub CameraGo(sender As Object, e As EventArgs)
If myCamera Is Nothing = False Then
Try
dummyImage.Visibility = Windows.Visibility.Visible
imageFileName = DateTime.Now.ToString("yyyyMMddHHmmss") & ".jpg"
recordDate = DateTime.Now.ToString("yyyy/MM/dd/HH:mm:ss")
myCamera.CaptureImage()
Catch
Me.Dispatcher.BeginInvoke(Sub() MessageBox.Show("エラーが発生しましたが、データは保存されています。"))
End Try
End If
End Sub
「フォルダ」アイコンがタップされた時の処理
これから作成するDataIchiranPage.xamlに遷移します。
Private Sub ListGo(sender As Object, e As EventArgs)
NavigationService.Navigate(New Uri("/DataIchiranPage.xaml", UriKind.Relative))
End Sub
「×」のクリアアイコンがタップされた時の処理
InkPresenterに配置された漢字画像を一気に削除します。
Private Sub ClearGo(sender As Object, e As EventArgs)
For i As Integer = 0 To imageList.Count - 1
Inkpresenter1.Children.Remove(imageList(i))
Next
End Sub
ListBoxから漢字画像が選択された時の処理
選択されたインデックスをメンバ変数kanjiIndexに格納しておきます。
Private Sub ListBox1_SelectionChanged(sender As Object, e As System.Windows.Controls.SelectionChangedEventArgs) Handles ListBox1.SelectionChanged
kanjiIndex = ListBox1.SelectedIndex
End Sub
漢字画像が選択され、InkPresenterがダブルタップされた時の処理
新しい、MouseDragElementBehaviorクラスのインスタンスbehaviorオブジェクトを作成します。MouseDragElementBehaviorクラスは、アタッチされた要素を、マウスのドラッグのジェスチャに応答して要素の上に再配置するクラスです。
Attachメソッドで指定されたオブジェクトにアタッチします。この場合、ListBoxから選択され配置された画像にアタッチすることになるため、配置された画像は、どの場所にでもドラッグが可能になります。
メンバ変数kanjiIndexに対応する
InkPresenterのダブルタップされたXの座標値をpositionXに、Y座標をpositionYに格納しておきます。myImageオブジェクトのWidthとHeightを指定し、SetValueメソッドで、LeftPropertyとTopPropertyの値を指定します。ダブルタップした中心に画像が表示されるよう、WidthとHeightそれぞれの値の半分を差し引いています。
Sourceプロパティには、Imageフォルダ内のkanjiNameに格納されている画像名を指定します。Tagプロパティには、配置される画像の個数を指定しておきます。
各プロパティの設定されたmyImageオブジェクトをInkPresenterに追加し、同時にImageのリストであるimageListにも追加しておきます。このimageListの値は×アイコンで画像を全部消去する場合に使用しています。
AddHandlerステートメントで、myImageオブジェクトがタップされた時のイベントハンドラを追加します。
新しいCompositeTransformクラスのインスタンスmyCompsiteオブジェクトを作成します。タップするごとに画像を45度ずつ回転させ、その回転角をmyCompsiteオブジェクトのRotationプロパティに指定します。
タップされた画像のRenderTransformにCompsiteTransformのオブジェクトmyCompsiteを指定します。これで、InkPresenterに配置された画像をタップすると45度ずつ回転するようになりました。Attachメソッドで指定されたオブジェクト(myImage)にアタッチします。
Private Sub Inkpresenter1_DoubleTap(sender As Object, e As System.Windows.Input.GestureEventArgs) Handles Inkpresenter1.DoubleTap
If kanjiIndex < 0 Then
MessageBox.Show("漢字画像を選択してください。")
Exit Sub
End If
Dim behavior As New MouseDragElementBehavior
Dim kanjiName As String = xmldoc.Descendants("image")(kanjiIndex).Value
myImage = New Image
positionX = e.GetPosition(Inkpresenter1).X
positionY = e.GetPosition(InkPresenter1).Y
With myImage
.Width = 100
.Height = 100
.SetValue(Canvas.LeftProperty, positionX - 50)
.SetValue(Canvas.TopProperty, positionY - 50)
.Source = New BitmapImage(New Uri("Image/" & kanjiName, UriKind.Relative))
.Tag = myIndex
End With
myIndex = myIndex + 1
InkPresenter1.Children.Add(myImage)
imageList.Add(myImage)
AddHandler myImage.Tap, Sub(imageSender As Object, imageArgs As System.Windows.Input.GestureEventArgs)
Dim tapPositionX = imageArgs.GetPosition(InkPresenter1).X
Dim tapPositionY = imageArgs.GetPosition(InkPresenter1).Y
Dim no As Integer = CInt(DirectCast(imageSender, Image).Tag)
imageList(no).SetValue((Canvas.LeftProperty), tapPositionX - 50)
imageList(no).SetValue((Canvas.TopProperty), tapPositionY - 50)
Dim myComposite As New CompositeTransform
myComposite.CenterX = 50
myComposite.CenterY = 50
If myAngle > 360 Then myAngle = 0
myAngle = myAngle + 45
myComposite.Rotation = myAngle
imageList(no).RenderTransform = myComposite
End Sub
behavior.Attach(myImage)
End Sub
ページがアクティブでなくなった時の処理
myIndexを0で初期化し、myCameraオブジェクトの関連付けを破棄します。
Protected Overrides Sub OnNavigatingFrom(e As System.Windows.Navigation.NavigatingCancelEventArgs)
myIndex = 0
myCamera = Nothing
MyBase.OnNavigatingFrom(e)
End Sub
End Class
カメラ撮影時に装飾アイテムを追加するサンプル


