連載
» 2013年11月06日 18時00分 公開

2カ月で160本作った還暦開発者が送る10のアプリ開発ノウハウ(3):選択した画像をトリミングして保存・削除・一括削除するには (3/4)

[薬師寺国安,PROJECT KySS]

ロジックコードを記述する(MainWindow.xaml.vb)

 次に、[ソリューション・エクスプローラー]内の「MainPage.xaml」を展開して表示される、「MainPage.xaml.vb」のコードを記述する。

 ここでは、コードが長くなるため今回のテーマと肝となる「画像の切り出し」「データの一覧表示」部分の解説にとどめている。名前空間の読み込み、メンバー変数の宣言も省略している。全てのコードを見る場合は、サンプルをダウンロードして「MainPage.xaml.vb」ファイルを見ていただきたい。

ページがアクティブになった時の処理(OnNavigatedToメソッド処理)

    Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
        Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
        Dim SubFolder = Await myFolder.CreateFolderAsync("TrimingImage", CreationCollisionOption.OpenIfExists)
        myPictureFiles = Await SubFolder.GetFilesAsync
        If myPictureFiles.Count > 0 Then
            allDeleteButton.IsEnabled = True
            deleteButton.IsEnabled = True
        Else
            allDeleteButton.IsEnabled = False
            deleteButton.IsEnabled = False
        End If
        Await AddPhoto()
        MyBase.OnNavigatedTo(e)
    End Sub

 以降、上記コードの中身を詳細に見ていこう。

 まず、非同期処理で行われるためメソッドの先頭にAsyncを追加している。

 次に、ピクチャライブラリ―にアクセスし、CreateFolderAsyncメソッドで「TrimingImage」というフォルダーを作成。その際、CreationCollisionOption.OpenIfExistsを指定すると、同名フォルダーがあるときは、そのフォルダー名を返し、無い時は新規に作成する。CreationCollisionOption Enumerationの詳細については、下記のURLを参照してほしい。

 GetFilesAsyncメソッドでは「TrimingImage」フォルダー内のファイルを取得し、メンバー変数myPictureFilesで参照しておく。

 Countプロパティで、そのフォルダー内のファイルの数を取得し、ファイルが存在する場合は、「削除」と「一括削除」ボタンの使用を可能にする。それ以外は使用を不可とする。

 切り出した画像が保存されている場合は、GridViewに画像の一覧を表示する、AddPhotoタスクを実行するようにしている。

ドラッグした時の処理(ContentPanel_ManipulationDeltaメソッドの処理)

Private Sub ContentPanel_ManipulationDelta(sender As Object, e As ManipulationDeltaRoutedEventArgs) 'Handles ContentPanel.ManipulationDelta
        'エラー処理をしていないとトリミング時にエラーが発生する
        Try
            Rectangle1.Width = e.Cumulative.Translation.X
            Rectangle1.Height = e.Cumulative.Translation.Y
        Catch
            e.Handled = False
            Exit Sub
        End Try
    End Sub

 ここでは、e.Cumulative.Translation.Xとe.Cumulative.Translation.Yでx-y画面座標を取得し、RectangleのWidthとHeightに設定している。矩形で描かれたWidthとHeightの値を、RectangleのWidthとHeightの値に指定するということだ。

タッチを開始した時の処理(ContentPanel_ManipulationStartedメソッドの処理)

    Private Sub ContentPanel_ManipulationStarted(sender As Object, e As ManipulationStartedRoutedEventArgs) 'Handles ContentPanel.ManipulationStarted
        Rectangle1.Margin = New Thickness(e.Position.X, e.Position.Y, 0, 0)
        Rectangle1.Width = 0
        Rectangle1.Height = 0
        If flag = True Then
            Rectangle1.Visibility = Windows.UI.Xaml.Visibility.Collapsed
            Button1.IsEnabled = False
        Else
            Rectangle1.Visibility = Windows.UI.Xaml.Visibility.Visible
            Button1.IsEnabled = True
        End If
    End Sub

 ここでは、RectangleのMarginプロパティに、タッチした位置に合わせてRectangleを移動した位置を指定している。Marginプロパティの書式は下記の通りだ。

FrameworkElement.Margin(left,,top, right, buttom)

 例えば、Margin="10"は全てのプロパティが10に設定される。またMargin="10,20"はLeftとRightが10、TopとBottomが20に設定されたThicknessとして解釈される。またMarginプロパティの値は「New Thickness(Left、Top、Right、Bottom)」と指定する。この場合はleftにe.Position.Xを、topにe.Position.Yの値を指定している。

 RectangleのWidthとHeightは「0」で初期化する。

 メンバー変数flagの値は、条件分岐を行う。Trueであった場合、つまり一覧表示されている切り出された画像が画面上に表示されていた場合は、トリミングができないように、Rectangle1を非表示にし、「切り出す」ボタンの使用を不可とする。それ以外の場合は、トリミングを可能にし、「切り出す」ボタンの使用を可能にしている。

矩形上でドラッグした時の処理(Rectangle1_ManipulationDeltaメソッドの処理)

    Private Sub Rectangle1_ManipulationDelta(sender As Object, e As ManipulationDeltaRoutedEventArgs) 'Handles Rectangle1.ManipulationDelta
        Rectangle1.Margin = New Thickness(Rectangle1.Margin.Left + e.Delta.Translation.X, Rectangle1.Margin.Top + e.Delta.Translation.Y, 0, 0)
        e.Handled = True
    End Sub

 ここでは、矩形の位置をドラッグした移動量分だけ動かす。e.Delta.Translation.X、e.Delta.Translation.Yでx-y画面座標の変化を取得し、e.Handled=Trueと指定して、Gridにイベントが伝わらないようにしている。

「画像選択」ボタンがタップされた時に実行されるSelectImageタスク処理

    Private Async Function SelectImage() As Task
        Dim myFileOpenPicker As New FileOpenPicker
        myFileOpenPicker.ViewMode = PickerViewMode.Thumbnail
        myFileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary
        myFileOpenPicker.FileTypeFilter.Add(".png")
        myFileOpenPicker.FileTypeFilter.Add(".jpg")
        myFile = Await myFileOpenPicker.PickSingleFileAsync()
        If myFile Is Nothing = False Then
            flag = False
            FileName = myFile.Path
            myBmp.SetSource(Await myFile.OpenReadAsync)
            Image1.Source = myBmp
            If myBmp.PixelWidth <> 640 OrElse myBmp.PixelHeight <> 480 Then
                Dim myMessage As New MessageDialog("画像サイズは640×480限定です。切り出しはできません。")
                Await myMessage.ShowAsync
                Image1.Source = Nothing
                Button1.IsEnabled = False
           Else
            Button1.IsEnabled = False
        End If
        End If
    End Function

 以降、上記コードの中身を詳細に見ていこう。

 まず、非同期処理で行われるためメソッドの先頭にAsyncを追加している。

 次に、FileOpenPickerクラスの新しいインスタンスmyFileOpenPickerオブジェクトを作成する。FileOpenPickerクラスは、ユーザーが選択し、ファイルを開くことのできるUI要素を表すクラスだ。

 ファイルの表示モードを指定するViewModeプロパティには、サムネイル表示を指定する。ちなみに、サムネイル表示のほかにリスト(List)表示がある。

 ファイルを開く最初の場所を設定する、SuggestedStartLocationプロパティにピクチャライブラリーを指定しておこう。開くファイルタイプを指定するFileTypeFilter.Addで「.png」と「.jpg」を指定し、PNGファイルを指定する場合は、FileTyleFilter.Add(".png")と指定する。"*.png"ではないので注意してほしい。

 PickSingleFileAsynメソッドでは、ユーザーが1つのファイルを選択できるようにファイルピッカーを表示し、変数myFileで参照する。変数myFileがファイルを参照している場合は、メンバー変数flagにFalseを指定し、トリミングを可能にし、選択された画像のファイルシステムパス名(myFile.Path)を、メンバー変数FileNameに格納しておく。

 新しいBitmapImageクラスのインスタンスmyBmpオブジェクトに、SetSourceメソッドで「Await myFile.OpenReadAsync」と指定して、ファイルの内容を読み込むために、現在のファイルを、ランダムアクセスストリームで開く。

 Image1のSourceプロパティには、myBmpオブジェクトを指定。これで、ファイルピッカーで指定したファイルが表示される。もし指定したファイルの画像サイズが640×480より大きかった場合は、メッセージを表示し、画像は表示せず、「切り取り」ボタンの使用も不可とする。

 また、画像を選択する際、選択をやめてキャンセルした場合も、「切り取り」ボタンの使用は不可としておく。

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。