タッチパネルでドラッグ&ドロップを使う汎用的なサンプル
アニメーションを作成する。
画像を削除する時のアニメーションを作成します。
「オブジェクトとタイムライン(B)」に表示されているオブジェクトの一覧からdelImageを選択し、[+]アイコンをクリックして新しいストリーボードを作成します。「名前(キー)」にはdelStoryboardと指定します。
アートボードが赤い枠線で囲まれ、タイムラインの記録がオンになった状態で、再生ヘッドを0位置に合わせ、[変換]パネル内の、RenderTransformのAngleの値に90を指定します。
次に、黄色の再生ヘッドを0.2の位置に合わせ、delImageを上方に少し移動し、[変換]パネルにあるRenderTransformのAngleの値に360を指定します。続けて再生ヘッドの位置を移動して0.4に合わせ、delImageを少し上方に移動し、Angleの値を0に指定します。
最後に再生ヘッドを0.6の位置に合わせて、delImageをListBoxの中央あたりまで移動し、Angleに270を指定し、1.0の位置で90を指定します。表示されている「愛」画像は横に寝ていますが、カメラで写した画像は縦向きで撮影するとデフォルトで横向きに表示されますので、最後は横向きとしておきます(図10)。
delImageにはプログラムから画像を指定するため、アニメーション作成時には何も表示されず、作成しづらい面がありますので、ダミーでSourceプロパティに画像を読み込んで作成しています。最終的には、Sourceプロパティには何も指定しないでください。プログラム上からSourceプロパティに表示される画像を指定します。
図10:delImageのストリーボードを作成している(クリックで拡大) |
次に、削除確認メッセージが表示されて[OK]が表示された場合に、画像の透明化を0にして消滅させるアニメーションを作成します。
「オブジェクトとタイムライン(B)」に表示されている、オブジェクトの一覧からdelImageを選択し、[+]アイコンをクリックして新しいストーリーボードを作成します。「名前(キー)」にはdelStoryboard2と指定します。アートボードが赤い枠線で囲まれ、タイムラインが記録オンの状態になりますので、ここで●をクリックして、一時記録をオフにします。そして、delImageを選択してListBoxの中央まで移動します。RenderTransformのAngleに90と指定しておきます。図11の状態にしておきます。
図11:delStoryboard2の記録を開始する最初の画面(クリックで拡大) |
●をクリックし記録オンの状態にします。黄色の再生ヘッドを1まで移動し、プロパティの[外観]パネルにあるOpacityに0と指定します。1秒かけて画像が消滅するアニメーションができました。
次に、削除確認メッセージが表示されて[キャンセル]が表示された場合、画像をカバンの中まで移動するアニメーションを作成します。
「オブジェクトとタイムライン(B)」に表示されている、オブジェクトの一覧からdelImageを選択し、[+]アイコンをクリックして新しいストーリーボードを作成します。
「名前(キー)」にはnoDeleteStoryboardと指定します。アートボードが赤い枠線で囲まれ、タイムラインの記録がオンになります。この前に作成したdelStoryboard2の状態で画像が表示されていますので、そのまま再生ヘッドを1まで移動し、delImageをカバンの画像の中まで移動します(図12)。ダミーで読み込んでいた「愛」の画像はカバンの背景に隠れて見えなくなります。
図12:delImageをカバンの画像の中まで移動する(クリックで拡大) |
最後にdelImageのSourceプロパティに指定していたダミーの画像を削除します。
Expression Blendを終了してVS2010に戻ります。
次に、DeletePage.xamlを展開して表示されるDeletePage.xaml.vbをダブルクリックして、リスト6のコードを記述します。
ロジックコードを記述する
リスト6 (DeletePage.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プロパティを定義しておきます。
Public Class _ImageInfo Property imageFileName As WriteableBitmap End Class Partial Public Class DeletePage Inherits PhoneApplicationPage Public Sub New() InitializeComponent() End Sub Dim myIndex As Integer = 0
ページがアクティブになった時呼び出されるメソッド
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.CombineでPictureDataというフォルダと、imageFileList.xmlというXMLファイル名と連結し、変数filePathに格納します。
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数myStreamを用意し、IsolatedStorageFileクラスのOpenFileメソッドで、PictureDataフォルダ内のimageFileList.xmlファイルを、指定したファイルアクセスを使用して指定したモードで開きます。
開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り変数readXmldoc変数に格納しておきます。
読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。_ImageInfoクラス型の新しいリストであるmyImageInfoオブジェクトを作成します。
Descendantsメソッドで、子孫要素である全ての
Path.CombineでPictureDataフォルダと、
WriteableBitmap型の変数imageSourceを宣言し、PictureDecoder.DecodeJpegメソッドで、開いたストリームをJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。PictureDecoder.DecodeJpegメソッドはMicrosof.Phone名前空間に属しています。WriteableBitmapクラスは書き込み更新することのできるBitmapSourceを提供するクラスです。
_ImageInfoクラスのimageFileNameプロパティに、読み込んだWriteableBitmapオブジェクトのimageSourceオブジェクトを指定し、AddメソッドでmyImageInfoオブジェクトに追加していきます。
ListBox1のItemsSourceプロパティにmyImageInfoオブジェクトを指定します。ListBoxのSetValueメソッドにZIndexPropertyを指定し、値に0を指定します。ListBoxコントロールが全てのオブジェクトの背面に表示されます。これで、撮影した画像の一覧が表示されます。
Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs) Try Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim filePath As String = Path.Combine("PictureData", "imageFileList.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 Dim doc As XElement = XElement.Parse(readXmldoc) Dim myImageInfo As New List(Of _ImageInfo) For Each result In From c In doc.Descendants("fileName") Select c Dim imageFilePath As String = Path.Combine("PictureData", result.Attribute("imageFileName").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) myImageInfo.Add(New _ImageInfo With {.imageFileName = imageSource}) End Using Next ListBox1.ItemsSource = myImageInfo ListBox1.SetValue(Canvas.ZIndexProperty, 0) End Using End Using MyBase.OnNavigatedTo(e) Catch Exit Sub End Try End Sub
ListBoxから任意の画像が選択された時の処理
ListBoxの選択された項目のインデックスが、0または0より大きい(きちんと画像が選択された)場合は、ListBoxの選択されたインデックスをメンバ変数myIndexに格納しておきます。カバンの中に隠れているdelImage1のSetValueメソッドにZIndexPropertyを指定し、値に10を指定して一番前面に表示されるようにします。delImage1に選択された画像を表示するDeleteImageプロシージャを実行し、カバンから画像が回転しながら飛び出してくるdelStoryboardを開始します。
Private Sub ListBox1_SelectionChanged(sender As Object, e As System.Windows.Controls.SelectionChangedEventArgs) Handles ListBox1.SelectionChanged If ListBox1.SelectedIndex => 0 Then myIndex = ListBox1.SelectedIndex delImage1.SetValue(Canvas.ZIndexProperty, 10) DeleteImage() delStoryboard.Stop() delStoryboard.Begin() Else Exit Sub End If End Sub
delImageコントロールにListBoxから選択された画像を表示させる処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineで、PictureDataというフォルダとimageFileList.xmlというXMLファイル名と連結し、変数filePathに格納します。
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数myStreamを用意し、IsolatedStorageFileクラスのOpenFileメソッドで、PictureDataフォルダ内のimageFileList.xmlファイルを、指定したファイルアクセスを使用して、指定したモードで開きます。開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドで、ファイルの最後まで読み取り変数readXmldoc変数に格納しておきます。
ListBoxより選択されたインデックスに該当する
変数imageStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数streamを用意し、IsolatedStorageFileクラスのOpenFileメソッドでPictureDataフォルダ内のdelImage変数に格納されている画像ファイルを、指定したファイルアクセスを使用して、指定したモードで開きます。
WriteableBitmap型の変数imageSourceを宣言し、PictureDecoder.DecodeJpegメソッドで、開いたストリームをJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。PictureDecoder.DecodeJpegメソッドはMicrosof.Phone名前空間に属しています。WriteableBitmapクラスは書き込み更新することのできるBitmapSourceを提供するクラスです。
delImage1のSourceプロパティに、読み込んだWriteableBitmapオブジェクトのimageSourceオブジェクトを指定します。ZIndexPropertyに10を指定し最前面に表示されるようにします。
Private Sub DeleteImage() Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim filePath As String = Path.Combine("PictureData", "imageFileList.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 Dim doc As XElement = XElement.Parse(readXmldoc) Dim delImage As String = doc.Descendants("fileName")(ListBox1.SelectedIndex).Attribute("imageFileName").Value Dim imageFilePath As String = Path.Combine("PictureData", delImage) Dim imageStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Using stream As IsolatedStorageFileStream = imageStorage.OpenFile(imageFilePath, FileMode.Open, FileAccess.Read) Dim imageSource As WriteableBitmap = PictureDecoder.DecodeJpeg(stream, 480, 640) delImage1.Source = imageSource delImage1.SetValue(Canvas.ZIndexProperty, 10) End Using End Using End Using End Sub
削除用の画像がカバンから回転しながら飛び出す、delStoryboardが完了した時の処理
選択した画像を削除するかどうかの確認メッセージを表示します。[ok]の場合は、画像が透明化されて消えていくdelStoryboard2を開始します。[キャンセル]の場合は、画像がカバンの中に戻っていくnoDeleteStoryboardを開始します。
Private Sub delStoryboard_Completed(sender As Object, e As System.EventArgs) Handles delStoryboard.Completed Dim kakunin = MessageBox.Show("この画像を削除しますか?", "削除確認", MessageBoxButton.OKCancel) Select Case kakunin Case MessageBoxResult.OK delStoryboard2.Stop() delStoryboard2.Begin() Exit Select Case Else ListBox1.SelectedIndex = -1 noDeleteStoryboard.Stop() noDeleteStoryboard.Begin() Exit Select End Select End Sub
画像の透明化アニメーションが完了した時の処理
削除処理のDeleteImageDataプロシージャを実行します。その後、透明化された画像がカバンの中に戻るよう、noDeleteStoryboardを開始します。この動作は目には見えません。
Private Sub delStoryboard2_Completed(sender As Object, e As System.EventArgs) Handles delStoryboard2.Completed DeleteImageData() noDeleteStoryboard.Begin() End Sub
画像がカバンの中に戻るアニメーションが完了した時の処理
delImage1に表示されている画像をカバンの背面に位置します。画像の透明化を解除します。
Private Sub noDeleteStoryboard_Completed(sender As Object, e As System.EventArgs) Handles noDeleteStoryboard.Completed delImage1.SetValue(Canvas.ZIndexProperty, 0) delImage1.Opacity = 1 End Sub
画像が削除される処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.CombineでPictureDataというフォルダとimageFileList.xmlというXMLファイル名と連結し、変数filePathに格納します。
imageFileList.xmlが存在する場合は、分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数myStreamを用意します。そして、IsolatedStorageFileクラスのOpenFileメソッドで、PictureDataフォルダ内のimageFileList.xmlファイルを、指定したファイルアクセスを使用して、指定したモードでファイルを開きます。
開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り変数readXmldoc変数に格納しておきます。読み取ったreadXmldocをXElement.Parseメソッドで文字列として読み込みます。
読み込んだXMLから、ListBoxの選択されたインデックスに該当する
ListBoxの選択されたインデックスに該当する
Private Sub DeleteImageData() Dim delXml As XElement Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim filePath As String = Path.Combine("PictureData", "imageFileList.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("fileName")(myIndex).Attribute("imageFileName").Value storage.DeleteFile(delImage) Dim delElement = delXml.Descendants("fileName")(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("fileName") Select c If allDeleteQuery.Count <= 0 Then storage.DeleteFile(filePath) NavigationService.Navigate(New Uri(String.Format("/MainPage.xaml?date={0}", DateTime.Now.ToShortDateString & DateTime.Now.ToLongTimeString), UriKind.Relative)) Exit Sub End If NavigationService.Navigate(New Uri(String.Format("/DeletePage.xaml?date={0}", DateTime.Now.ToShortDateString & DateTime.Now.ToLongTimeString), UriKind.Relative)) Else Exit Sub End If End Sub End Class
タッチパネルでドラッグ&ドロップを使うサンプル