カメラで写した写真を分離ストレージに保存し、写真の一覧を表示する
2011年11月22日(火)
ImageコントロールをButtonコントロールに変換する
まずカメラの画像の表示されているImage1を選択し、マウスの右クリックで表示されるメニューから、「コントロールの作成」を選択します(図4)。
図4:Image1を選択し、マウスの右クリックで表示されるメニューから「コントロールの作成」を選択(クリックで拡大) |
「コントロールの作成」画面が表示されます。「コントロールの種類」からButtonを選択し、「定義先」の「アプリケーション」にチェックを付け[OK]ボタンをクリックします(図5)。「名前(キー)」はデフォルトのままにしておきます。
図5:「コントロールの種類」にButtonを選択し、「定義先」の「アプリケーション」をチェックする(クリックで拡大) |
中央にButtonと表示されたカメラの画像が表示されます。Buttonの文字を選択し、[Delete]キーで削除します。アートボードの上部に表示されている[Button]タブをクリックします。コントロールを配置した画面が表示されます。カメラの画像を選択して、プロパティを見ると「種類」がButtonになっています。「名前」にcameraButtonと指定します(図6)。
図6:ImageがButtonに変換されている。Buttonの名前にcameraButtonと指定する(クリックで拡大) |
同様にImage2のフロッピーの画像もButtonに変換します。Buttonの名前はsaveButtonとします。
Expression Blendを終了してVS2010に戻ります。
ImageからButtonに変換した箇所がリスト2のように書き出されています。
リスト2 ImageからButtonに変換されたXAMLコード(MainPage.xamlの一部)
(1)cameraButtonとsaveButtonにStyle属性が持たされ、ButtonStyle1とButtonStyle2を参照しています。ButtonStyle1とButtonStyle2はApp.xaml内で定義されています。saveButtonは最初の状態では、使用不可とするため、IsEnabledのチェックを外しておきます。写真が撮影された時点でsaveButton(フロッピーのアイコン)の使用を可能とします。 (2)ichiranButton(データ一覧)も最初の状態では使用不可としておきます。 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Image Height="356" HorizontalAlignment="Left" Margin="40,71,0,0" x:Name="Image" Stretch="Fill" VerticalAlignment="Top" Width="398" /> <TextBox Height="91" HorizontalAlignment="Left" Margin="-6,485,0,0" x:Name="commentTextBox" VerticalAlignment="Top" Width="456" TextWrapping="Wrap" AcceptsReturn="True" /> <Button Content="データ一覧" Height="72" HorizontalAlignment="Left" Margin="6,624,0,0" Name="ichiranButton" VerticalAlignment="Top" Width="444" IsEnabled="False" /> ■(2) <TextBlock Height="31" HorizontalAlignment="Left" Margin="12,463,0,0" Name="TextBlock1" Text="コメント" VerticalAlignment="Top" Width="131" /> <TextBlock Height="33" HorizontalAlignment="Left" Margin="140,18,0,0" Name="TextBlock2" Text="カメラは縦向きのみの対応です" VerticalAlignment="Top" Width="298" FontWeight="Bold" Foreground="Crimson" /> <Button x:Name="cameraButton" Content="Button" HorizontalAlignment="Left" Height="45" Margin="40,6,0,0" Style="{StaticResource ButtonStyle1}" VerticalAlignment="Top" Width="61"/> ■(1) <Button x:Name="saveButton" Content="Button" HorizontalAlignment="Left" Height="46" Margin="190,572,0,0" Style="{StaticResource ButtonStyle2}" VerticalAlignment="Top" Width="50" IsEnabled="False" /> ■(1) </Grid>
次に、MainPage.xamlを展開して表示される、MainPage.xaml.vbをダブルクリックしてリスト3のコードを記述します。
ロジックコードを記述する
リスト3 (MainPage.xaml.vb)
Option Strict On ランチャーやチューザーに関するクラスの含まれる、Microsoft.Phone.Tasks名前空間をインポートします。 Imports Microsoft.Phone.Tasks Imports System.Windows.Media.Imaging Imports Microsoft.Phone 仮想ファイルシステムを作成および使用するための型が含まれている、System.IO.IsolatedStorage名前空間をインポートします。分離ストレージによって、安全なクライアント側のストレージが提供されます。 Imports System.IO.IsolatedStorage Imports System.IO Imports System.Xml.Linq Partial Public Class MainPage Inherits PhoneApplicationPage ' コンストラクター Public Sub New() InitializeComponent() End Sub カメラアプリケーションを起動するCameraCaptureTaskクラス用メンバ変数、myCameraTaskを宣言します。 Dim myCameraTask As CameraCaptureTask バイト型の配列、imageByteをメンバ変数として宣言します。 Dim imageByte As Byte()
ページがアクティブになった時呼び出されるメソッド
新しいCameraCaptureTaskクラスのインスタンス、myCameraTaskオブジェクトを生成します。 AddHandlerステートメントで、チューザータスクが完了した時に発生するCompletedイベントに、イベントハンドラを追加します。イベントハンドラ内では以下の処理が実行されます。 カメラアプリケーションが起動して、写真がきちんと撮れた場合は、CompositeTransformクラスの新しいインスタンスmyCompositeを生成します。CompositeTransformクラスは、1 つのオブジェクトに複数の異なる変換を適用することができるクラスです。CenterXとCenterYプロパティに、撮った画像が表示されるImageコントロールのWidthとHeightの約半分のサイズを指定します。CenterXプロパティでは、CompositeTransform で指定された、全ての変換の中心点の x 座標を設定します。CenterYプロパティでは、CompositeTransform で指定された、全ての変換の中心点の y 座標を設定します。回転を表すRotationに90を指定します。カメラを縦向きで撮った画像もデフォルトでは横向きに表示されるため、90度回転して縦向きに表示されるようにしています。 写真のデータを含む、ストリームのバイトの長さを取得し、バイト配列を作成します。Readメソッドで、現在のストリームからバイトシーケンスを読み取り、読み取ったバイト数でストリーム内の位置を進めます。 ChosenPhoto.Readの書式は以下の通りです。 ChosenPhoto.Read(バイト配列,0ベースのバイトオフセット(現在のストリームから読み取ったデータの格納を開始します),現在のストリームから読み取るバイトの最大数) Seekメソッドで、現在のストリーム内の位置を設定します。書式は以下の通りです。 ChosePhoto.Seek(基点のパラメーターのバイト オフセット, 参照ポイントを示すSeekOrigin型の値) SeekOriginにはBeginを指定し、ストリームの先頭を指定しています。 WriteableBitmap型の変数imageSourceを宣言し、PictureDecoder.DecodeJpeg(resultArgs.ChosenPhoto)で、撮った写真をJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。PictureDecoder.DecodeJpegメソッドはMicrosof.Phone名前空間に属しています。WriteableBitmapクラスは書き込み更新することのできるBitmapSourceを提供するクラスです。 ImageのRenderTransformプロパティにmyCompsiteオブジェクトを指定して、Image領域を90度回転し、Sourceプロパティに、デコードされたJPEGファイルを格納しているimageSourceオブジェクトを指定します。これでImage内に撮った写真が表示されます。 カメラを起動している状態で撮影を中止した場合は、saveButton(フロッピーのアイコン)は使用不可のままで処理を抜けます。 変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineメソッドで、2つの文字列を1つのパスに結合します。ここでは、myPicturesフォルダとimageFileList.xmlを結合しています。myPictures\imageFileList.xmlとなります。この値をfilePath変数に格納します。FileExistsメソッドでfilePathに格納したファイルが存在する場合は、ichiranButton(データ一覧)の使用を可能にします。そうでない場合は、不可のままです。 Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs) myCameraTask = New CameraCaptureTask AddHandler myCameraTask.Completed, Sub(resultSender As Object, resultArgs As PhotoResult) If resultArgs.TaskResult = TaskResult.OK Then Dim myComposite As New CompositeTransform myComposite.CenterX = 189 myComposite.CenterY = 173 myComposite.Rotation = 90 imageByte = New Byte(CInt(resultArgs.ChosenPhoto.Length)) {} resultArgs.ChosenPhoto.Read(imageByte, 0, imageByte.Length) resultArgs.ChosenPhoto.Seek(0, IO.SeekOrigin.Begin) Dim imageSource As WriteableBitmap = PictureDecoder.DecodeJpeg(resultArgs.ChosenPhoto) Image.RenderTransform = myComposite Image.Source = imageSource Else saveButton.IsEnabled = False Exit Sub End If End Sub Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim xmlFilePath As String = Path.Combine("myPictures", "imageFileList.xml") If storage.FileExists(xmlFilePath) = True Then ichiranButton.IsEnabled = True Else ichiranButton.IsEnabled = False End If MyBase.OnNavigatedTo(e) End Sub
カメラアイコンがタップされた時の処理
Showメソッドでカメラアプリケーションを起動します。saveButton(フロッピーのアイコン)の使用を可能にします。 Private Sub cameraButton_Click(sender As Object, e As System.Windows.RoutedEventArgs) Handles cameraButton.Click myCameraTask.Show() saveButton.IsEnabled = True End Sub
フロッピーのアイコン(保存)がタップされた時の処理
保存する画像のファイル名を作成します。現在の「年月日時間分秒.jpg」のファイル名とし、imageFileNameに格納しておきます。 変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。DirectryExistsメソッドでmyPicturesというフォルダが存在しているかどうかをチェックし、存在していない場合は、CreateDirectoryメソッドでmyPicturesというフォルダを作成します。 Path.CombineでmyPicturesというフォルダとimageFileNameに格納されている画像名とを連結します。 分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数myStream変数を用意し、IsolatedStorageFile.CreateFileメソッドで、分離ストレージ内にfilePath変数の持っているフォルダ名付き画像ファイル名を作成します。次に、IsolatedStorageFileStream.Writeメソッドで、バイト配列から読み取ったデータを使用して、IsolatedStorageFileStreamオブジェクトにバイトのブロックを書き込みます。 IsolatedStorageFileStream.Writeメソッドの書式は下記の通りです。 IsolatedStorageFileStream.Write(書き込むバッファ, 開始位置を示すバッファ内のバイト オフセット,書き込む最大バイト数) 変数xmlStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineで、myPicturesというフォルダ名と、imageFileList.xmlという画像ファイル名を記録したXMLファイルを連結し、xmlFilePath変数に格納しておきます。 myPicturesというフォルダ内にimageFileList.xmlが存在していない場合は、Visual Basic の埋め込み式を用いて、XML宣言とルート要素image、その子要素としてfileName、属性にimageFileNameを指定し、埋め込み式の構文である <%= expression %>を用いてimageFileName変数の値を指定します。また、fileNameの内容テキストには、commentTextBox.Textの値を指定します。これは ASP.NET で使用される構文と同じです。 分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数xmlStreamを用意し、IsolatedStorageFile.CreateFileメソッドで、分離ストレージ内にxmlFilePath変数の持っているフォルダ名付きXMLファイルを作成します。 imageFileList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。Writeメソッドで埋め込み式のXMLをストリームに書き込みます。保存した旨のメッセージを表示します。 次は、既にmyPicturesフォルダ内にimageFileList.xmlが存在する場合の処理です。 IsolatedStorageFileクラスのOpenFileメソッドでmyPicturesフォルダ内のimageFileList.xmlファイルを、指定したファイルアクセスを使用して指定したモードでファイルを開きます。開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り変数readXmldoc変数に格納しておきます。読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。 追加するfileName要素を、埋め込み式を用いて生成し、imageFileName属性の値や、fileNameの内容テキストを指定します。新しく生成したXML要素を、読み込んだXMLにAddメソッドで追加します。IsolatedStorageFileStreamを閉じます。 imageFileList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。Writeメソッドで新しいXML要素が追加されたXMLを、ストリームに書き込みます。保存した旨のメッセージを表示します。 「データ一覧」ボタンの使用を可能にし、コメント欄と画像の表示されていた領域をクリアします。フロッピーアイコンの保存ボタンの使用を不可とします。 Private Sub saveButton_Click(sender As Object, e As System.Windows.RoutedEventArgs) Handles saveButton.Click '画像の保存 Dim imageFileName As String=DateTime.Now.ToString("yyyyMMddHHmmss") & ".jpg" Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication If storage.DirectoryExists("myPictures") = False Then storage.CreateDirectory("myPictures") End If Dim filePath As String = Path.Combine("myPictures", imageFileName) Using myStream As IsolatedStorageFileStream = storage.CreateFile(filePath) myStream.Write(imageByte, 0, imageByte.Length) End Using 'XMLファイルの保存 Dim xmlStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim xmlFilePath As String = Path.Combine("myPictures", "imageFileList.xml") If xmlStorage.FileExists(xmlFilePath) = False Then Dim xmldoc As XDocument = <?xml version="1.0" encoding="utf-8"?> <image> <fileName imageFileName=<%= imageFileName %>><%= commentTextBox.Text %></fileName> </image> 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)) xmlwriter.Flush() xmlwriter.Write(xmldoc.ToString) End Using MessageBox.Show("画像とXMLファイルを保存しました。") 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 = <fileName imageFileName=<%= imageFileName %>><%= commentTextBox.Text %></fileName> doc.Add(addXml) xmlStream.Close() If xmlStorage.FileExists(xmlFilePath) = True Then Using xmlwriter As StreamWriter = New StreamWriter(storage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Write)) xmlwriter.Flush() xmlwriter.Write(doc.ToString) MessageBox.Show("画像とXMLファイルを保存しました。") End Using End If End Using End If ichiranButton.IsEnabled = True commentTextBox.Text = String.Empty Image.Source = Nothing saveButton.IsEnabled = False End Sub
[データ一覧]ボタンがクリックされた時の処理
これから作成する、DataShowPage.xamlに遷移します。 Private Sub ichiranButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles ichiranButton.Click NavigationService.Navigate(New Uri("/DataShowPage.xaml", UriKind.Relative)) End Sub End Class
「カメラで写した写真を分離ストレージに保存し、写真の一覧を表示する」サンプルプログラム
連載バックナンバー
Think ITメルマガ会員登録受付中
Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。