カメラ撮影時に装飾アイテムを追加するサンプル
![](https://thinkit.co.jp/sites/default/files/styles/main_image_730x/public/main_images/3577_main_7.png?itok=BvoHoNRl)
次に、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
カメラ撮影時に装飾アイテムを追加するサンプル