PR

時刻とともに、その日の出来事をキャラクターが音声で教えてくれるアプリを作る

2014年3月19日(水)
薬師寺 国安

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

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

リスト2 (MainWindow.xaml.vb)

‘ アプリケーションがスレッドプールを使用して作業項目を実行できるようにするクラスの含まれる、
‘ Windows.System.Threading名前空間をインポートします。

Imports Windows.System.Threading

Imports Windows.UI

‘ XML ドキュメント オブジェクト モデルのサポートを提供するクラスの含まれる、Windows.Data.Xml.Dom名前空間を
‘ インポートします。

Imports Windows.Data.Xml.Dom

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

Imports Windows.Storage

‘ シーケンシャルアクセスストリームおよびランダムアクセスストリームに対する読み取りと書きこみをサポートするクラスの含まれる
‘ Windows.Storage.Streams名前空間をインポートします。

Imports Windows.Storage.Streams

Imports Windows.UI.Popups

‘ 最新の HTTP アプリケーションのプログラミング インターフェイスを提供するクラスの含まれる、

‘ System.Net.Http 名前空間をインポートします。

Imports System.Net.Http Public NotInheritable Class MainPage Inherits Page Private myColorInfo As String ‘ 指定した時間の間隔で、指定した優先順位に処理されるDispacherキューに統合されているタイマーを表す、新しい、 ‘ DispatcherTimerクラスのインスタンスである、myTimerメンバー変数を宣言します。

Private myTimer As New DispatcherTimer Private myFont As String ‘ キャラクタが読み上げる内容を格納する文字列型のメンバー変数readingTextを宣言します。

Private readingText As String ‘ 乱数を発生させるRandomクラス型のメンバー変数Rndを宣言します。

Private Rnd As Random Private myMonth As String Private myDate As String

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

メンバー変数myMonthに現在の月を格納しておきます。メンバー変数myDateに現在の日付を格納しておきます。
ピクチャライブラリ—のXMLサブフォルダにアクセスします。
GetFilesAsyncメソッドでフォルダ内のファイルを取得して、コレクション変数myFileに格納します。ファイルの数が0より大きかった場合は、GetFileAsyncメソッドでXMLフォルダ内のmyColor.xmlを取得し、xmldoc変数で参照します。
xmldocで参照しているファイルを、

Await xmldoc.OpenAsync(FileAccessMode.Read)

と記述して読み取り専用で開き、myStreamで参照します。StreamReaderの新しいインスタンスreaderオブジェクトを作成し、myStreamで参照しているファイルと、エンコーディングUTF8で初期化します。Reader.ReadToEndでファイルを最後まで読み取り変数resultに格納します。
XElement.Parseメソッドでresult変数の内容を文字列として読み取ります。
変数_Colorに要素の値を、変数_Fontに 要素の値を格納します。
_Color変数の値によって条件分岐を行います、RedからSalmonであった場合の処理を記述します。例えば_Colorが「Red」であった場合は、CascadingTextBlock1の文字色を「Red」に指定し、redRadioButtonにチェックを付けます。他の色の場合も同じ処理を行います。
CascadingTextBlock1のTextプロパティに現在の時刻を表示します。

CascadingTextBlock1.Text = DateTime.Now.ToString()

と指定しています、これを

CascadingTextBlock1.Text = DateTime.Now.ToString(“yyyy年MM月dd日HH時mm分ss秒”)

と指定するとエラーになります。CascadingTextBlockが日本語表示に対応していないようです。
CasCadingTextBlockのFontFamilyに_Font変数の値を指定します。myTimerのIntervalに10秒を指定します。
AddHandlerステートメントで、タイマー間隔が経過すると発生するTickイベントにイベントハンドラを指定します。

イベントハンドラ内では以下の処理を行います。
新しいRandomクラスのインスタンスRndを作成します。最少が「1」で最大が「5」のランダムな数字を発生させます。しかし最大値の「5」は含まれませんので、実際には「1~4」のランダムな数字が作成されます。
変数myRndの値で条件分岐を行います。「1~4」の間のランダムな数字に合わせてsyokoImageのSourceプロパティに、ソリューション・エクスプローラー内のImageフォルダに配置していたPNG画像を指定します。
CascadingTextBlock1に現在の時刻を表示し、キャラクタに喋らす内容をメンバー変数readingTextに格納します。
キャラクタが音声を発して喋るsyokoVoiceタスクを実行します。CascadingTextBlock1のAnimatedLoadedにTrueを指定し、

Await CascadingTextBlock1.BeginCascadingTransitionAsync()

と指定しカスケーディングトラジッションを伴って時刻の表示を開始します。
非同期処理で行われるためメソッドの先頭にAsyncを追加します。

  Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    myMonth = DateTime.Now.Month.ToString
    myDate = DateTime.Now.Day.ToString
    Dim result As String
    Dim _Color As String
    Dim _Font As String = String.Empty
    Dim myStorageFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
    Dim mySubFolder = Await myStorageFolder.CreateFolderAsync("XML", CreationCollisionOption.OpenIfExists)
    Dim myFile = Await mySubFolder.GetFilesAsync()
    If myFile.Count > 0 Then
      Dim xmldoc As StorageFile = Await mySubFolder.GetFileAsync("myColor.xml")
      Using myStream As IRandomAccessStream = Await xmldoc.OpenAsync(FileAccessMode.Read)
        Using reader As StreamReader = New StreamReader(myStream.AsStream, System.Text.Encoding.UTF8)
          result = reader.ReadToEnd
        End Using
      End Using
      Dim doc As XElement = XElement.Parse(result)
      _Color = doc.Descendants("Color").Value
      _Font = doc.Descendants("Font").Value
      Select Case _Color
        Case "Red"
          CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Red)
          redRadioButton.IsChecked = True
        Case "Blue"
          CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Blue)
          blueRadioButton.IsChecked = True
        Case "Green"
          CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Green)
          greenRadioButton.IsChecked = True
        Case "White"
          CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.White)
          whiteRadioButton.IsChecked = True
        Case "Yellow"
          CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Yellow)
          goldRadioButton.IsChecked = True
        Case "Pink"
          CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Pink)
          pinkRadioButton.IsChecked = True
        Case "Salmon"
          CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Salmon)
          salmonRadioButton.IsChecked = True
      End Select
    End If
    CascadingTextBlock1.Text = DateTime.Now.ToString()
    If _Font <> String.Empty Then CascadingTextBlock1.FontFamily = New FontFamily(_Font)
    myTimer.Interval = New TimeSpan(0, 0, 10)
 
    AddHandler myTimer.Tick, Async Sub()
                              Rnd = New Random
                              Dim myRnd = Rnd.Next(1,5)
                              Select Case myRnd
                                Case 1
                                  syokoImage.Source = New BitmapImage(New Uri("ms-appx:///Image/syoko1.png", UriKind.Absolute))
                                  Exit Select
                                Case 2
                                  syokoImage.Source = New BitmapImage(New Uri("ms-appx:///Image/syoko2.png", UriKind.Absolute))
                                  Exit Select
                                Case 3
                                  syokoImage.Source = New BitmapImage(New Uri("ms-appx:///Image/syoko3.png", UriKind.Absolute))
                                  Exit Select
                                Case 4
                                  syokoImage.Source = New BitmapImage(New Uri("ms-appx:///Image/syoko4.png", UriKind.Absolute))
                                  Exit Select
                              End Select
                              CascadingTextBlock1.Text = DateTime.Now.ToString
                              readingText = DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") & "です。"
                              Await syokoVoice()
                              CascadingTextBlock1.AnimateOnLoaded = True
                              Await CascadingTextBlock1.BeginCascadingTransitionAsync()
                            End Sub
    myTimer.Start()
  End Sub

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

タイマーを停止します。

  Protected Overrides Sub OnNavigatedFrom(e As Navigation.NavigationEventArgs)
    myTimer.Stop()
    MyBase.OnNavigatedFrom(e)
  End Sub

redRadioButtonがチェックされた時の処理

CascadingTextBlock1の文字色を「Red」に指定します。メンバー変数myColorInfoに「Red」を格納します。
これ以後のpinkRadioButton_Checkedまでの処理も、この処理と同じですので、説明は省略します。

  Private Sub redRadioButton_Checked(sender As Object, e As RoutedEventArgs) Handles redRadioButton.Checked
    CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Red)
    myColorInfo = "Red"
  End Sub
 
  Private Sub blueRadioButton_Checked(sender As Object, e As RoutedEventArgs) Handles blueRadioButton.Checked
    CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Blue)
    myColorInfo = "Blue"
  End Sub
 
  Private Sub greenRadioButton_Checked(sender As Object, e As RoutedEventArgs) Handles greenRadioButton.Checked
    CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Green)
    myColorInfo = "Green"
  End Sub
 
  Private Sub whiteRadioButton_Checked(sender As Object, e As RoutedEventArgs) Handles whiteRadioButton.Checked
    CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.White)
    myColorInfo = "White"
  End Sub
 
  Private Sub goldRadioButton_Checked(sender As Object, e As RoutedEventArgs) Handles goldRadioButton.Checked
    CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Yellow)
    myColorInfo = "Yellow"
  End Sub

  Private Sub salmonRadioButton_Checked(sender As Object, e As RoutedEventArgs) Handles salmonRadioButton.Checked
    CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Salmon)
    myColorInfo = "Salmon"
  End Sub
 
  Private Sub pinkRadioButton_Checked(sender As Object, e As RoutedEventArgs) Handles pinkRadioButton.Checked
    CascadingTextBlock1.Foreground = New SolidColorBrush(Colors.Pink)
    myColorInfo = "Pink"
  End Sub

[保存]アイコンがタップされた時の処理

XElement型の変数colorXmlを宣言し、埋め込み式の構文である を用いて要素の値に、myColorInfo、の値にmyFontを指定して格納します。
XElement.ParseメソッドでcolorXml.ToStringの内容を文字列として読み込みます。変数resultにXMLの内容を格納します。
ピクチャライブラリ—内のXMLサブフォルダにアクセスします。CreatFileAsyncメソッドでXMLサブフォルダ内にmyColor.xmlファイルを作成し、myXmlFileで参照します。
OpenAsyncメソッドでmyXmlFileを読み込み書きこみ専用で開き、変数myStreamで参照します。
myStreamオブジェクトで初期化された新しいDataWriterのインスタンスwriterを作成します。DataWriterクラスは、データを出力ストリームに書きこむクラスです。ユニコードにUtf8を指定し、WriteStringメソッドでXMLのデータを格納しているresult変数の内容を書きこみます。StoreAsyncメソッドでパッキングストアにバッファーのデータをコミットします。
保存した旨のメッセージを表示します。

  Private Async Sub saveButton_Click(sender As Object, e As RoutedEventArgs) Handles saveButton.Click
    Dim colorXml As XElement = <ColorInfo><Color><%= myColorInfo %></Color><Font><%= myFont %></Font></ColorInfo>
    Dim xmldoc As XElement = XElement.Parse(colorXml.ToString)
    Dim result As String = xmldoc.ToString
    Dim myStorageFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
    Dim mySubFolder = Await myStorageFolder.CreateFolderAsync("XML", CreationCollisionOption.OpenIfExists)
    Dim myXmlFile As StorageFile = Await mySubFolder.CreateFileAsync("myColor.xml", CreationCollisionOption.ReplaceExisting)
     
    Using myStream As IRandomAccessStream = Await myXmlFile.OpenAsync(FileAccessMode.ReadWrite)
      Dim writer As DataWriter = New DataWriter(myStream)
      writer.UnicodeEncoding = UnicodeEncoding.Utf8
      writer.WriteString(result)
      Await writer.StoreAsync
    End Using
    Dim message As New MessageDialog("設定を保存しました。")
    Await message.ShowAsync
  End Sub

CountDownControlが最後までカウントされた時の処理

syokoImageを表示状態にします。CountDownControlを非表示にします。

  Private Sub CountdownControl1_CountdownComplete(sender As Object, e As RoutedEventArgs) Handles CountdownControl1.CountdownComplete
    syokoImage.Visibility = Xaml.Visibility.Visible
    CountdownControl1.Visibility = Xaml.Visibility.Collapsed
  End Sub

フォントを表示しているComboBoxからフォントが選択された時の処理

変数myComboItemにComboBoxから選択された項目名を格納します。
選択された項目名によって、条件分岐を行います。例えば、「文字2」が選択された場合は、メンバー変数myFontにソリューション・エクスプローラー内のFontフォルダにあるフォント名を次のように指定します。

myFont = "Font/YournameS7FinancialFull.ttf#YournameS7FinancialFull"

Fontフォルダの前に”ms-appx:///”の記述はこの場合不要です。あっても問題はありません。「文字5」までに対して同様の処理を行います。
CascadingTextBlock1のFontFamilyにmyFontを指定すると、指定されたフォントで時刻が表示されます。

  Private Sub fontComboBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles fontComboBox.SelectionChanged
    Dim myComboItem = DirectCast(fontComboBox.SelectedItem, ComboBoxItem).Content.ToString
    Select Case myComboItem
      Case "文字1"
        myFont = "Font/meiryob.ttc#meiryob"
        Exit Select
      Case "文字2"
        myFont = "Font/YournameS7FinancialFull.ttf#YournameS7FinancialFull"
        Exit Select
      Case "文字3"
        myFont = "Font/YournameS12Financial.ttf#YournameS12Financial"
        Exit Select
      Case "文字4"
        myFont = "Font/YournameL12SquareRoundFull.ttf#YournameL12SquareRoundFull"
        Exit Select
      Case "文字5"
        myFont = "Font/YournameS7FinancialHalf.ttf#YournameS7FinancialHalf"
        Exit Select
    End Select
    CascadingTextBlock1.FontFamily = New FontFamily(myFont)
  End Sub

時刻をキャラクタが音声で喋る処理

MediaElement型のmyMedia変数を宣言し、MediaElement1で初期化しておきます。
音声機能へのアクセスを提供する、新しいSpeechSynthesizerのインスタンス、synthオブジェクトを作成します。
SynthesizeTextToStreamAsyncメソッドで、指定した文字列から、音声出力を非同期に生成します。
SetSourceメソッドで、指定されたストリームおよびMIME型を使用してSourceプロパティを設定します。Playメソッドで音声を再生します。
音声にどんな言語で、どのような声で喋らすかは、SpeechSynthesizerのVoiceプロパティで参照できます。下記のURLを参照してください。
> SpeechSynthesizer.Voice | voice property

上記URLを見るとJapanese JA は性別が「Female」で、名前は「Haruka」という女性が読み上げるようです。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

  Private Async Function syokoVoice() As Task
    Dim myMedia As MediaElement = Me.MediaElement1
    Dim synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer
    Dim stream = Await synth.SynthesizeTextToStreamAsync(readingText)
    myMedia.SetSource(stream, stream.ContentType)
    myMedia.Play()
  End Function

CascadingTextBlockのアニメ—ションが完了した場合の処理

「今日は何の日」APIを使用します。下記のURLを参照してください。
> 「今日は何の日」API

変数whatsDayUriに

String.Format("http://www.mizunotomoaki.com/wikipedia_daytopic/api.cgi/{0}", myMonth & "/" & myDate)

と指定します。引数に現在の月(myMonth)と現在の日(myDate)をスラッシュ(/)で連結して指定します。
新しいHttpClientのインスタンスmyHttpClientオブジェクトを作成します。
GetStringAsyncメソッドで、指定したURIにGET要求を送信し、非同期処理で応答本体を文字列として取得し、変数resultに格納します。
XElement.Parseメソッドで変数resultの値を文字列として読み込みます。
変数queryにインデックスが0番目の要素の子要素要素の子要素の値を格納します。インデックスが0番目を指定しているには、「今日は何の日」APIで取得されるデータは非常に多く存在するため、ここでは1件のデータを表示させるために、インデックス0番目、つまり一番初めのデータだけを選択しています。
MessageTextBlockにquery変数の内容を表示します。

  Private Async Sub CascadingTextBlock1_CascadeCompleted(sender As Object, e As EventArgs) Handles CascadingTextBlock1.CascadeCompleted
    Dim whatsDayUri = String.Format("http://www.mizunotomoaki.com/wikipedia_daytopic/api.cgi/{0}", myMonth & "/" & myDate)
    Dim myHttpClient As New HttpClient
    Dim result = Await myHttpClient.GetStringAsync(whatsDayUri)
    Dim xmldoc As XElement = XElement.Parse(result)
    Dim query = xmldoc.Descendants("kinenbi_detail")(0).Elements("item").Elements("description").Value
    MessageTextBlock.Text = "今日は【" & query & "】の日です。"
  End Sub
End Class

今回はここまでです。また次回の記事でお会いしましょう。

Think IT会員限定特典
  • 時刻とともに、その日に起こった出来事をキャラクターが教えてくれるアプリ

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

薬師寺国安事務所代表。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のWebサイトにログインすることでさまざまな限定特典を入手できるようになります。

Think IT会員サービスの概要とメリットをチェック

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