画面上を流れる数字を暗算して正解を求めるアプリを作ろう(その2)

2014年1月28日(火)
薬師寺 国安

このアプリについて

前回に引き続き「動体視力暗算アプリの解説を行います。前回はデザイン画面やXAMLコードの解説がメインでしたが、今回はVBコードの解説がメインになります。前編(その1)と併用して読まれることを希望します。
まずは、「モジュールの設定」から入ります。

モジュールの設定

VS2013メニューの[プロジェクト]−[モジュールの追加]と選択して、全てのプロジェクトから参照できるモジュール変数を宣言します。このアプリは5画面ありますから、どの画面からでも共通にアクセスできるよう、手っ取り早くモジュールで宣言しておきます(リスト1)。モジュール変数を使用すると可読性が悪くなり、バグの温床になる云々がいわれますが、それは使い手の問題であって、臨機応変に使用すれば、なんらモジュール変数を使用しても問題はありません。却って便利なくらいです。

リスト1 Module.vb

Module Module1
‘ 背景色を設定するためのパブリック変数
  Public myBackGroundColor As SolidColorBrush

‘ 背景画像を指定するためのパブリック変数
  Public myBackGroundImage As BitmapImage
  Public backGroundValue As String
  Public backGroundIndex As Integer

‘ レベル値を格納するパブリック変数
  Public myLevel As Integer

‘ 桁数を格納するパブリック変数
  Public keta As Integer = -1
  Public ketaValue As String

‘ スピードを格納するパブリック変数
  Public speed As Integer = -1
  Public speedValue As String

‘ 文字サイズを格納するパブリック変数
  Public mojiSize As Integer = -1
  Public mojiSizeValue As String

‘ 数字の合計を格納するパブリック変数
  Public Total As Integer = 0

‘ DispatcherTimerクラス型のパブリック変数myTimer(タイマーを操作する変数)
  Public myTimer As DispatcherTimer
 
  Public myScore As Integer
  Public SumTotal As Integer = 0
  Public calcNo As Integer = 0
End Module

次に、ソリューション・エクスプローラー内のMainWindow.xamlを展開して表示される、MainWindow.xaml.vbをダブルクリックしてリスト2のコードを記述します。

ロジックコードを記述する

リスト2 (MainWindow.xaml.vb)

Imports Windows.UI.Popups

‘ ファイル、フォルダおよびアプリケーションの設定を管理するクラスの含まれる、
‘ Windows.Storage名前空間をインポートします。
Imports Windows.Storage
Public NotInheritable Class MainPage
  Inherits Page

ページがアクティブになった時の処理

新しいDispacherTimerのインスタンスmyTimerオブジェクトを作成します。
SettingPage(設定画面)に遷移します。

  Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
    myTimer = New DispatcherTimer
    myFrame.Navigate(GetType(SettingPage))
    MyBase.OnNavigatedTo(e)
  End Sub

[開始]ボタンがタップされた時の処理

各設定項目が選択されていない場合は、警告メッセージを表示して処理を抜ける。
各設定項目が選択されている場合は、設定した内容で、文字が左から右に流れてくるgoPageに遷移する。

  Private Async Sub playButton_Click(sender As Object, e As RoutedEventArgs) Handles playButton.Click
    If keta < 0 OrElse speed < 0 OrElse mojiSize < 0 Then
      Dim message As New MessageDialog("設定項目を選択してください。")
      Await message.ShowAsync
      Exit Sub
    Else
      backButton.Visibility = Windows.UI.Xaml.Visibility.Visible
      myFrame.Navigate(GetType(goPage))
      playButton.IsEnabled = False
      stopButton.IsEnabled = True
    End If
  End Sub

[停止]ボタンがタップされた時の処理

タイマーを停止します。
[停止]ボタンをタップすると、スコアの計算は、何回文字が流れてきたかによって計算しているので、文字が流れてきた回数をmyScoreで加算します。流れてきた文字の合計をSumTotalで合計します。

  Private Sub stopButton_Click(sender As Object, e As RoutedEventArgs) Handles stopButton.Click
    myTimer.Stop()
    myScore = myScore + 1
    SumTotal = SumTotal + Total
 
    playButton.IsEnabled = True
    resultButton.IsEnabled = True
    stopButton.IsEnabled = False
  End Sub

[設定画面に戻る]アイコンがタップされた時の処理

各変数を0で初期化します。TitleTextBlockに「設定」と表示します。タイマーが実行されている場合は、タイマーを停止します。
タイマーが停止されている場合は、[設定画面に戻る]アイコンを非表示にし、[開始]ボタンを使用可能にします。各項目の設定を行うSettingPageに遷移します。

  Private Sub backButton_Click(sender As Object, e As RoutedEventArgs) Handles backButton.Click
    Total = 0
    SumTotal = 0
    calcNo = 0
    TitleTextBlock.Text = "設定"
    If myTimer.IsEnabled = True Then
       myTimer.Stop()
    Else
      backButton.Visibility = Windows.UI.Xaml.Visibility.Collapsed
      playButton.IsEnabled = True
      resultButton.IsEnabled = False
      stopButton.IsEnabled = False
      myScore = 0
      myFrame.Navigate(GetType(SettingPage))
    End If
  End Sub

[結果]ボタンがタップされた時の処理

タイマーを停止し、[設定画面に戻る]アイコンを表示状態にし、TitleTextBlockに「動体視力暗算」と表示します。答えを入力するInputAnswerPageに遷移します。

  Private Sub resultButton_Click(sender As Object, e As RoutedEventArgs) Handles resultButton.Click
    myTimer.Stop()
    backButton.Visibility = Windows.UI.Xaml.Visibility.Visible
    playButton.IsEnabled = False
    stopButton.IsEnabled = False
    TitleTextBlock.Text = "動体視力暗算"
    myFrame.Navigate(GetType(InputAnswerPage))
  End Sub

[戦績]ボタンをタップした時の処理

ピクチャライブラリ—内のMentalArithmeticXMLフォルダーにアクセスします。フォルダー内のファイルをGetFilesAsyncメソッドで取得して、コレクション変数saveFilesに格納します。
MentalArithmeticXMLフォルダー内にファイルが存在する場合は、戦績を表示するResultShowPageに遷移します。ファイルが存在しない場合はメッセージを表示して処理を抜けます。

  Private Async Sub recordPointButton_Click(sender As Object, e As RoutedEventArgs) Handles recordPointButton.Click
    Dim myStorageFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
    Dim mySubFolder = Await myStorageFolder.CreateFolderAsync("MentalArithmeticXML", CreationCollisionOption.OpenIfExists)
    Dim saveFiles = Await mySubFolder.GetFilesAsync
    TitleTextBlock.Text = "動体視力暗算"
    If saveFiles.Count > 0 Then
      backButton.Visibility = Windows.UI.Xaml.Visibility.Visible
      playButton.IsEnabled = False
      resultButton.IsEnabled = False
      myFrame.Navigate(GetType(ResultShowPage))
    Else
      Dim message As New MessageDialog("保存されたデータはありません!")
      Await message.ShowAsync
      Exit Sub
    End If
  End Sub
End Class

次に、ソリューション・エクスプローラー内のSettingPage.xamlを展開して表示される、SettingPage.xaml.vbをダブルクリックしてリスト3のコードを記述します。

ロジックコードを記述する

リスト3 (SettingPage.xaml.vb)

‘ ユーザーインターフェースに関するクラスの含まれるWindows.UI名前空間をインポートします。

Imports Windows.UI Public NotInheritable Class SettingPage Inherits Page

「桁数」が選択された時の処理

モジュール変数ketaにketaListBoxから選択されたインデックス番号を格納します。
モジュール変数ketaValueにketaListBoxから選択された項目を格納します。

  Private Sub ketaListBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ketaListBox.SelectionChanged
    Try
      speedListBox.IsEnabled = True
      keta = ketaListBox.SelectedIndex
      ketaValue = DirectCast(ketaListBox.SelectedItem, ListBoxItem).Content.ToString
    Catch
      Exit Sub
    End Try
  End Sub

「スピード」が選択された時の処理

モジュール変数speedにspeedListBoxから選択されたインデックス番号を格納します。
モジュール変数speedValueにspeedListBoxから選択された項目を格納します。

  Private Sub speedListBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles speedListBox.SelectionChanged
    Try
      mojiSizeListBox.IsEnabled = True
      speedListBox.IsEnabled = False
      speed = speedListBox.SelectedIndex
      speedValue = DirectCast(speedListBox.SelectedItem, ListBoxItem).Content.ToString
    Catch
      Exit Sub
    End Try
  End Sub

「文字サイズ」が選択された時の処理

モジュール変数myLevelに、「桁数」のListBoxから選択されたインデックスに1を加算した値と、「スピード」のListBoxより選択されたインデックスに1を加算した値、「文字サイズ」のListBoxより選択されたインデックスに1を加算した値、これら3つの値を乗算して格納しておきます。
モジュール変数mojiSizeValueにmojiSizeListBoxより選択された項目を格納しておきます。
mojiSizeListBoxより選択されたインデックスで条件分岐を行い、モジュール変数mojiSizeに値を格納します。「大」を選択した場合は、文字サイズは「72」、「小」を選択すれば文字サイズは「18」となります。

  Private Sub mojiSizeListBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles mojiSizeListBox.SelectionChanged
    Try
      speedListBox.IsEnabled = False
      mojiSizeListBox.IsEnabled = False
      settingResetButton.IsEnabled = True
      myLevel = (ketaListBox.SelectedIndex + 1) * (speedListBox.SelectedIndex + 1) * (mojiSizeListBox.SelectedIndex + 1)
      levelTextBlock.Text = myLevel
      mojiSizeValue = DirectCast(mojiSizeListBox.SelectedItem, ListBoxItem).Content.ToString
      Select Case mojiSizeListBox.SelectedIndex
        Case 0
          mojiSize = 72
        Case 1
          mojiSize = 36
        Case 2
          mojiSize = 18
        End Select
      Catch
        Exit Sub
      End Try
    End Sub

[設定やり直し]ボタンをタップした時の処理

「桁数」のListBoxを選択可能にし、レベルの表示されていた領域をクリアし、ketaListBox、speedListBox、mojiSizeListBoxの選択を解除します。[設定やり直し]ボタンの使用を不可とします。

  Private Sub settingResetButton_Click(sender As Object, e As RoutedEventArgs) Handles settingResetButton.Click
    ketaListBox.IsEnabled = True
    levelTextBlock.Text = String.Empty
    ketaListBox.SelectedIndex = -1
    speedListBox.SelectedIndex = -1
    mojiSizeListBox.SelectedIndex = -1
    settingResetButton.IsEnabled = False
  End Sub

「背景」が選択された時の処理

モジュール変数backGroudValueにbackgroundListBoxより選択された項目を格納します。
モジュール変数backGroundIndexにbackgroundListBoxより選択されたインデックスを格納し、そのインデックス番号で条件分岐を行います。インデックスが0と1の時は背景色を変化させます。そのほかのインデックスの場合は背景画像が変化しますので、その処理は後述します。

  Private Sub backgroundListBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles backgroundListBox.SelectionChanged
    backGroundValue = DirectCast(backgroundListBox.SelectedItem, ListBoxItem).Content.ToString
    backGroundIndex = backgroundListBox.SelectedIndex
    Select Case backGroundIndex
      Case 0
        myBackGroundColor = New SolidColorBrush(Colors.White)
        Exit Select
      Case 1
        myBackGroundColor = New SolidColorBrush(Colors.Gray)
        Exit Select
      End Select
    End Sub
  End Class
  • 動体視力暗算アプリ

    『Windows 8.1+Visual Studio 2013によるWindows ストア・アプリ開発実例集』 第4回のサンプルプログラムです。
薬師寺国安事務所

薬師寺国安事務所代表。Visual Basic プログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。1997年に薬師寺聖とコラボレーション・ユニット PROJECT KySS を結成。2003年よりフリーになり、PROJECT KySS の活動に本格的に参加、.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Windows Phoneアプリ開発を経て、現在はWindows ストア アプリを多数公開中

Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。Microsoft MVP for Development Platforms-Windows Platform Development (Oct 2014-Sep 2015)。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています