近くにある病院の場所をキャラクターが音声で教えてくれるアプリを作る

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

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

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

リスト2 (MainWindow.xaml.vb)

Option Strict On

‘ コンピュータの地理的位置にアクセスできるようにするクラスの含まれる、Windows.Devices.Geolocation名前空間を
‘ インポートします。
Imports Windows.Devices.Geolocation

‘ Bing Mapsに関するクラスの含まれる、Bing.Maps名前空間をインポートします。
Imports Bing.Maps
Imports Windows.UI

‘ アプリケーションウィンドウやウィンドウ対話を作成し管理するサポートと、ウィンドウ上の入力イベントを処理する
‘ クラスの含まれる、Windows.UI.Core名前空間をインポートします。
Imports Windows.UI.Core

‘ 最新のHTTPアプリケーション用のプログラミング インターフェイスを提供するクラスの含まれる、
‘ System.Net.Http名前空間をインポートします。
Imports System.Net.Http

Imports Windows.UI.Popups

‘ HospitalInfoクラス内に文字列型の「病院名」、「住所」プロパティを定義しておきます。
Public Class HospitalInfo
  Public Property 病院名 As String
  Public Property 住所 As String
End Class

Public NotInheritable Class MainPage
  Inherits Page

‘ 現在の地理的位置にアクセスするクラスである、Geolocatorクラスのメンバー変数myGeolocatorを宣言します。
  Private myGeolocator As Geolocator

‘ Windowsランタイムコアイベントメッセージディスパッチャを提供するクラスである、
‘ CoreDispatcherクラスのメンバー変数、myCoreDispacherを宣言します。
  Private myCoreDispacher As CoreDispatcher

‘ YahooのアプリケーションIDで初期化された、メンバー定数変数AppIDを宣言します。
  Const AppID As String = " YahooのアプリケーションID"

‘ GoogleのAPI Keyで初期化された、メンバー定数変数GoogleAppIDを宣言します。
  Const GoogleAppID As String = " GoogleのAPI Key"
 
  Private flag As Boolean = False
  Private myCoodinatePosition As String = String.Empty
  Private myAddressCoodinatePosition As String = String.Empty
  Private myUri As String = String.Empty
  Private myAddressLatitude As String
  Private myAddressLongitude As String
  Private myAddressUri As String

‘ MediaElementクラス型のメンバー変数myMediaを宣言します。
  Private myMedia As MediaElement

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

場所にアクセスし、場所が変更された時や、Geolocatorの機能が変更された時に発生する各イベントで、イベントハンドラを実行するDataShowメソッドを実行します。

  Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    DataShow()
  End Sub

場所にアクセスし、場所が変更された時や、Geolocatorの機能が変更された時に発生する各イベントで、イベントハンドラを実行する処理。

myGeolocatorオブジェクトが作成されていない場合は、新しいGeolocatorクラスのインスタンスmyGeolocatorオブジェクトを作成します。
場所認識の精度レベルを表す、DesiredAccuracy プロパティにはDefaultを指定しておきます。
Default以外にHighがありますが、パフォーマンスが低下する恐れがありますので、Defaultを指定しています。

Window.Current.CoreWindow.Dispatcherで、現在アクティブになっている、ウィンドウの内部コアオブジェクトのイベントディスパッチャを取得して、myCoreDispacherで参照します。

AddHandlerステートメントで、場所が更新された時のPositionChangedイベント時のイベントハンドラ、myGeolocator_PositionChangedを追加します。

同じく、AddHandlerステートメントで、更新された場所を提供するGeolocatorの機能が変更された時に発生する、StatusChanged時のイベントハンドラ、myGeolocator_StatusChangedを追加します。

  Private Sub DataShow()
    If myGeolocator Is Nothing = True Then
      myGeolocator = New Geolocator
      myGeolocator.DesiredAccuracy = PositionAccuracy.Default
      myCoreDispacher = Window.Current.CoreWindow.Dispatcher
    End If
    AddHandler myGeolocator.PositionChanged, AddressOf myGeolocator_PositionChanged
    AddHandler myGeolocator.StatusChanged, AddressOf myGeolocator_StatusChanged
  End Sub

場所が変更された時の処理

myCoreDispacher.RunAsyncメソッドで、イベントディスパッチャを実行し、ディスパッチされたイベントの結果を非同期に返します。ディスパッチャの優先順位は標準のNormalを指定しています。

緯度と経度のデータや都市の住所データを含めることができるGeoPositionクラス型の変数posを宣言し、PositionChangedイベントに関連付けられた場所データを取得します。

Clearメソッドで、地図内を一度クリアしておきます。

新しいPushpinクラスのインスタンスmyPinオブジェクトを作成します。ピンの背景色を「Crimson」に指定します。

新しいLocationクラスのインスタンスmyLocationオブジェクトを作成します。Locationクラスは、地図上の場所の標高と座標値を含むクラスです。

ブール型のメンバー変数flagの値で条件分岐を行います。FlagがTrueであった場合、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。その場合は、Locationを、メンバー変数myAddressLatitudeに格納された緯度の値と、myAddressLongitudeに格納された経度の値で初期化します。

それ以外の場合は、現在位置の緯度(pos.Coordinate.Point.Position.Latitude)と経度(pos.Coordinate.Point.Position.Longitude)で初期化します。

MapLayerクラスのSetPositionメソッドで、マップレイヤー内にピンの位置を設定します。
myMapにAddメソッドでピンを追加します。
SetViewメソッドで、指定された中心部に位置、ズーム レベル、方位、およびピッチにマップビューを設定します。この場合myLocationの位置に16レベルでズームインします。

ブール型のメンバー変数flagの値で条件分岐を行います。FlagがTrueであった場合、つまり、現在位置の緯度(pos.Coordinate.Point.Position.Latitude)と経度(pos.Coordinate.Point.Position.Longitude)が取得されている場合の処理です。

メンバー変数myAddressUriに「Yahoo!ローカルサーチAPI」のアドレスを格納します。
引数latにpos.Coordinate.Point.Position.Latitudeの緯度の値を指定して、lonにpos.Coordinate.Point.Position.Longitudeの経度の値を指定します。
AppidにはYahooのアプリケーションIDを指定します。

そうでない場合、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。その場合は、Locationを、メンバー変数myAddressLatitudeに格納された緯度の値と、myAddressLongitudeに格納された経度の値で初期化します。

新しいHttpClientクラスのインスタンスmyAddressHttpClientオブジェクトを作成します。
HttpClientクラスは、URIで識別されるリソースにHTTP要求を送信し、そのリソースからHTTP応答を受信するための基本クラスが含まれています。

XML要素を表すXElementクラス型変数、xmldocを宣言します。

GetStringAsyncメソッドでmyAddressUriの結果を文字列として返し、変数myCurrentAddressに格納します。
GetStringAsyncメソッドは、指定URIにGET要求を送信し、非同期操作で応答本体を文字列として返すメソッドです。

返される文字列はXML形式になっています。このXMLのルート要素には、名前空間や不要な属性が定義されていて、内容を取得する際の邪魔(-_-;)になりますので、Replace関数で、ルート要素()だけに置換しています。

XElement.Parseメソッドで置換された結果のXMLが格納されている、myCurrentAddressを文字列として読み込みます。

読み込んだXMLから

要素の内容を取得して、変数myAddressに格納しておきます。

新しいStackPanelのインスタンスmyAddressStackPanelオブジェクトを作成します。
Marginプロパティに「5」を指定して余白を設けます。
背景色を「Navy」に指定します。
このStackPanelオブジェクトのインスタンスを、非表示としておきます。

新しいTextBlockのインスタンスmyAddressTextBlockオブジェクトを作成します。文字色を「Red」に指定します。
文字サイズに「24」を指定し、パディングに「5」を指定します。
ブール型のメンバー変数flagの値で条件分岐を行います。FlagがFalse であった場合は(つまり通常の処理をしていた場合)、myAddressTextBlockに変数myAddressと文字列「辺り」を連結して表示します。それ以外の場合は、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。その場合は、WatermarkTextBoxに入力された値と文字列「辺り」を連結して表示します。

myStackPanelオブジェクトにAddメソッドで住所の設定された、myAddressTextBlockを追加します。

SetValueメソッドでmyAddressStackPanelのZIndexPropertyに20を指定します。myAddressStackPanelが前面に表示されます。

ブール型のメンバー変数flagの値で条件分岐を行います。FlagがFalseであった場合、つまり通常の処理を行っていた場合は、MapLayerクラスのSetPositionメソッドで、マップレイヤー内にmyAddressStackPanelオブジェクトの位置を設定し、Locationを現在位置の緯度と経度で初期化して指定します。それ以外の場合、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。MapLayerクラスのSetPositionメソッドで、マップレイヤー内にmyAddressStackPanelオブジェクトの位置を設定し、Locationをメンバー変数myAddressLatitudeが格納している緯度とmyAddressLongitudeが格納している経度で初期化して設定します。
MapLayerクラスは、地図上の要素の位置を保持しているマップレイヤーを表すクラスです。

myMapにAddメソッドでmyAddressStackPanelオブジェクトを追加します。
myAddressStackPanelは最初の状態では非表示になっているのでわかりませんが、ピンの位置にStackPanelオブジェクトが表示されることになります。

AddHandlerステートメントでピンをタップした時のイベントハンドラを追加します。
イベントハンドラ内では、myAddressStackPanelオブジェクトを表示状態にします。

AddHandlerステートメントでmyAddressStackPanelをタップした時のイベントハンドラを追加します。イベントハンドラ内では、myAddressStackPanelオブジェクトを非表示状態にします。

ブール型のメンバー変数flagの値で条件分岐を行います。FlagがTrueであった場合、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。

病院の情報を取得するGoogleのWeb APIのURLを設定します。
「location」には緯度と経度をカンマで区切って格納している、メンバー数myAddressCoodinatePositionの値を指定します。
「radius」には範囲(メートル単位)を指定します。ここでは1000を指定して1Kmとしています。
「types」には、何を目的に検索するかを指定します。ここでは「Hospital」を指定しています。
この「types」には|(パイプ)で区切って複数の値を指定できます。指定できる値の一覧は下記のURLを参照してください。
> https://developers.google.com/places/documentation/supported_types

「Sensor」には場所の要求が位置センサー(GPS など)を使用してデバイスから来たかどうかを示す値を、trueまたはfalseで指定します。ここでは「true」を指定しています。
「language」には「ja」を「key」にはGoogleより取得した「APIKey」を指定します。

「OK」ボタンがクリックされていない場合は、locationに現在位置の緯度と経度をカンマで区切った値を格納している、メンバー変数myCoodinatePositionの値を指定します。

検索結果は最大で20件までの情報を取得して表示します。これらの式を変数myUriに格納しておきます。TitleTextBlockにWatermarkTextBlockに入力された値、または現在位置の住所を格納しているmyAddress変数の値と、文字列「辺り」を連結して表示します。

新しいHttpClientのインスタンスmyHttpClientを作成します。
GetStringAsyncメソッドでGoogleの Web APIの式を格納しているmyUriの内容を読み取り、返ってくるXMLを変数resultHospitalに格納します。
XElement.ParseメソッドでresultHospitalが格納しているXMLの内容を、文字列として読み込みます。

HospitalInfoクラス型である新しいリストのインスタンスmyHospitalInfoオブジェクトを作成します。

Descendantsメソッドで、全ての要素の内容を、変数resultに格納しながら、以下の処理を行います。

変数noの値を1ずつ増加させます。この値は、これから作成する「Webで表示」ボタンの、どのボタンがクリックされたかの判定に使用します。

新しいImageのインスタンスmyImageオブジェクトを作成します。Widthに「70」、Heightに「137」と指定し、Sourceプロパティにソリューション・エクスプローラー内のImagesフォルダ内にある「syoko1.png」を指定します。

新しいStackPanelのインスタンスsyokoStackPanelオブジェクトを作成します。背景色に「DarkGreen」を指定し、AddメソッドでmyImageオブジェクトを追加します。

新しいPushPinクラスのインスタンスhospitalPinオブジェクトを作成します。
背景色に「Crimson」を指定し、Textプロパティに1ずつ増加する変数noの値を指定します。
ピンの表面に連番が表示されます。

要素の子要素要素の子要素要素の値を変数myLongitude(経度)に、要素の子要素要素の子要素要素の値を変数myLatitude(緯度)に格納します。
地理的位置に関連付けされた緯度と経度で初期化された、新しいLocationクラスのインスタンスhospitalLocationオブジェクトを作成します。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内に病院のピンの位置を設定します。myMapにAddメソッドで病院の位置にピンを追加します。

新しいStackPanelのインスタンスresultStackPanelオブジェクトを作成し、Orientationに「Horizontal」と指定しておきます。

新しいStackPanelのインスタンスmyStackPanelオブジェクトを作成します。
Marginに「5」、背景色に「Navy」を指定し、非表示としておきます。

新しいTextBlockのインスタンスmyNameTextBlockを作成します。
文字色は「Red」、Widthに「400」、文字の回り込みは可、文字サイズは「24」、パディングは「5」と指定します。
Textプロパティには、文字列「病院名」と要素の内容を連結して指定します。病院の名前が表示されます。

新しいTextBlockのインスタンスaddressTextBlockを作成します。
文字色は「Beige」、Widthに「400」、文字の回り込みは可、文字サイズは「20」、パディングは「5」と指定します。
Textプロパティには、文字列「住所」と要素の値を連結して表示します。病院の住所が表示されます。

HositalInfoクラスの「病院」プロパティに連番と、要素の値を連結して指定します。「住所」プロパティには要素の値を指定し、AddメソッドでmyHospitalInfoオブジェクトに追加します。

新しいButtonのインスタンスmyButtonを作成します。
Contentプロパティに「Webで表示」と指定します。
Tagプロパティに変数noの値を指定します。どのButtonがクリックされたかの判定に使用します。

キャラクタに読み上げさせる文章の内容をメンバー変数readingAddressに格納しておきます。

SetValueメソッドで、myStackPanelのZIndexPropertyに変数noの値を指定します。myStackPanelがPushPinよりも前面に表示されるようになります。

myStackPanelオブジェクトにAddメソッドで、myNameTextBlock、addressTextBlock、myButtonを追加します。

resultStackPanelオブジェクトに、AddメソッドでmyStackPanelオブジェクト、syokoStackPanelオブジェクトを追加します。
resultStackPanelオブジェクトは非表示としておきます。

MapLayerクラスのSetPositionメソッドで、マップレイヤー内にresultStackPanelの位置を設定します。
myMapにAddメソッドでresultStackPanelを追加します。

AddHandlerステートメントで、病院の位置を表すピンがタップされた時のイベントハンドラを追加します。イベントハンドラ内では以下の処理を行います。
resultStackPanelオブジェクトが表示状態になり、病院の情報とキャラクタが表示されます。
メンバー変数myMediaを、MediaElement1で初期化しておきます。
音声機能へのアクセスを提供する、新しいSpeechSynthesizerのインスタンス、synthオブジェクトを作成します。
SynthesizeTextToStreamAsyncメソッドで、指定した文字列から、音声出力を非同期に生成します。
SetSourceメソッドで、指定されたストリームおよびMIME型を使用してSourceプロパティを設定します。Playメソッドで音声を再生します。
音声にどんな言語で、どのような声で喋らすかは、SpeechSynthesizerのVoiceプロパティで参照できます。下記のURLを参照してください。
> http://msdn.microsoft.com/en-us/library/windows.media.speechsynthesis.speechsynthesizer.voice.aspx
上記URLを見るとJapanese JA は性別が「Female」で、名前は「Haruka」という女性が読み上げるようです。
非同期処理で行われるため、イベントハンドラの先頭にAsyncを追加します。

AddHandlerステートメントで、病院の情報を表示した、resultStackPanelがタップされた時のイベントハンドラを追加します。
音声の読み上げを中止します。resultStackPanelオブジェクトが非表示になります。
AddHandlerステートメントで、Buttonがクリックされた時のイベントハンドラを追加します(Webで表示がクリックされた時の処理です)。
クリックされたButtonのTagの値を変数indexに格納します。数値にキャストしたindexの値をmyIndexに格納します。-1しているのは、ピンの表面に表示される連番は1から20まで表示されますが、XML要素のインデックスは0から始まるため-1しています。

変数mySendAddressにmyIndexに位置する要素の値と、myIndexに位置する要素の値を半角の空白で区切って格納しておきます。住所+半角の空白+病院名の値が格納されます。
これらの値はUri.EscapeDataStringメソッドでエスケープ表現に変換しておきます。
Windows.System.Launcher.LaunchUriAsyncメソッドで、指定されたURIのURIスキーム名に関連付けられている既定のアプリケーション(この場合はIE11のブラウザ)を起動します。

この記述方法では、Windows ストアの認定の要件「2.4 アプリで提供されるプライマリ エクスペリエンスはアプリ内で行われなければならない」に抵触し、ストアの審査には通りませんが、審査員のコメント欄に、「このアプリの目的は病院の位置をBing Maps上に表示させて、キャラクタに読み上げさせることにあります。ブラウザでの表示はあくまでも補助的手段です。」と記述しておけば、大体認定されますので、お試しください。

Try~Catch~End Tryで例外処理を行っています。例外が発生した場合はErrorShowプロシージャを実行します。

最後にGridView1のItemsSourceプロパティにmyHospitalInfoオブジェクトを追加します。これで、マウスの右クリックをした際に、アプリケーションバー内に病院の一覧が表示されます。
非同期処理で行われるためメソッドの先頭にAsyncを追加します。

  Private Async Sub myGeolocator_PositionChanged(sender As Geolocator, args As PositionChangedEventArgs)
    Dim myLatitude As String = String.Empty
    Dim myLongitude As String = String.Empty
    Dim no As Integer = 0
 
    Await myCoreDispacher.RunAsync(CoreDispatcherPriority.Normal, Async Sub()
      Dim pos As Geoposition = args.Position
      myMap.Children.Clear()
 
      Dim myPin As New Pushpin
      myPin.Background = New SolidColorBrush(Colors.Crimson)
  
      Dim myLocation As Location
      If flag = True Then
        myLocation = New Location(CDbl(myAddressLatitude), CDbl(myAddressLongitude))
      Else
        'Windows 8.1の新形式
        myLocation = New Location(CDbl(pos.Coordinate.Point.Position.Latitude), CDbl(pos.Coordinate.Point.Position.Longitude))
      End If
        MapLayer.SetPosition(myPin, myLocation)
        myMap.Children.Add(myPin)
        myMap.SetView(myLocation, 16)
        
      If flag = False Then
        myAddressUri = String.Format("http://reverse.search.olp.yahooapis.jp/OpenLocalPlatform/V1/reverseGeoCoder?lat={0}&lon={1}&appid={2}", pos.Coordinate.Point.Position.Latitude, pos.Coordinate.Point.Position.Longitude, AppID)
      Else
        myAddressUri = String.Format("http://reverse.search.olp.yahooapis.jp/OpenLocalPlatform/V1/reverseGeoCoder?lat={0}&lon={1}&appid={2}", myAddressLatitude, myAddressLongitude, AppID)
      End If
        Dim myAddressHttpClient As New HttpClient
        Dim myCurrentAddress As String = String.Empty
        Dim xmldoc As XElement
        Try
          myCurrentAddress = Await myAddressHttpClient.GetStringAsync(myAddressUri)
          myCurrentAddress = myCurrentAddress.Replace("<YDF firstResultPosition=" & ChrW(34) & "1" & ChrW(34) & " totalResultsAvailable=" & ChrW(34) & "1" & ChrW(34) & " totalResultsReturned=" & ChrW(34) & "1" & ChrW(34) & " xmlns=" & ChrW(34) & "http://olp.yahooapis.jp/ydf/1.0" & ChrW(34) & ">", "<YDF>")
          xmldoc = XElement.Parse(myCurrentAddress)
          Dim myAddress = xmldoc.Descendants("Address").Value
          Dim myAddressStackPanel As New StackPanel
          myAddressStackPanel.Margin = New Thickness(5)
          myAddressStackPanel.Background = New SolidColorBrush(Colors.Navy)
        myAddressStackPanel.Visibility = Xaml.Visibility.Collapsed
        Dim myAddressTextBlock As New TextBlock
        myAddressTextBlock.Foreground = New SolidColorBrush(Colors.Red)
        myAddressTextBlock.FontSize = 24
        myAddressTextBlock.Padding = New Thickness(5)
        If flag = False Then
          myAddressTextBlock.Text = myAddress & " 辺り"
        Else
          myAddressTextBlock.Text = WatermarkTextBox1.Text & " 辺り"
        End If
        myAddressStackPanel.Children.Add(myAddressTextBlock)
        myAddressStackPanel.SetValue(Canvas.ZIndexProperty, 20)

        If flag = False Then
          'Windows 8.1の新形式
          MapLayer.SetPosition(myAddressStackPanel, New Location(CDbl(pos.Coordinate.Point.Position.Latitude), CDbl(pos.Coordinate.Point.Position.Longitude)))
        Else
          MapLayer.SetPosition(myAddressStackPanel, New Location(CDbl(myAddressLatitude), CDbl(myAddressLongitude)))
        End If
        myMap.Children.Add(myAddressStackPanel)
 
      AddHandler myPin.Tapped, Sub()
        myAddressStackPanel.Visibility = Xaml.Visibility.Visible
      End Sub
 
      AddHandler myAddressStackPanel.Tapped, Sub()
        myAddressStackPanel.Visibility = Xaml.Visibility.Collapsed
      End Sub
 
        If flag = True Then
          myUri = String.Format("https://maps.googleapis.com/maps/api/place/nearbysearch/xml?location={0}&radius=1000&types=hospital|dentist|doctor&sensor=true&language=ja&key={1}", myAddressCoodinatePosition, GoogleAppID) ‘ (B)
          TitleTextBlock.Text = WatermarkTextBox1.Text & " 辺りの1Km範囲内の病院を表示"
        Else
          myCoodinatePosition = pos.Coordinate.Point.Position.Latitude & "," & pos.Coordinate.Point.Position.Longitude
          myUri = String.Format("https://maps.googleapis.com/maps/api/place/nearbysearch/xml?location={0}&radius=1000&types=hospital|dentist|doctor&sensor=true&language=ja&key={1}", myCoodinatePosition, GoogleAppID)
          TitleTextBlock.Text = myAddress & " 辺りの1Km範囲内の病院を表示"
        End If
          Dim myHttpClient As New HttpClient
          Dim resultHospital = Await myHttpClient.GetStringAsync(myUri)
          Dim httpDoc As XElement = XElement.Parse(resultHospital)
          Dim myHospitalInfo As New List(Of HospitalInfo)
          For Each result In From c In httpDoc.Descendants("result") Select c
            no += 1
            Dim myImage As New Image
            With myImage
              .Width = 70
              .Height = 137
              .Stretch = Stretch.UniformToFill
              .Source = New BitmapImage(New Uri("ms-appx:///Images/syoko1.png", UriKind.Absolute))
            End With
            Dim syokoStackPanel As New StackPanel
            syokoStackPanel.Background = New SolidColorBrush(Colors.DarkGreen)
            syokoStackPanel.Children.Add(myImage)
            Dim hospitalPin As New Pushpin
            hospitalPin.Background = New SolidColorBrush(Colors.Navy)
            hospitalPin.Text = no.ToString
 
            myLongitude = result.Element("geometry").Element("location").Element("lng").Value
            myLatitude = result.Element("geometry").Element("location").Element("lat").Value
            Dim hospitalLocation = New Location(CDbl(myLatitude), CDbl(myLongitude))
            MapLayer.SetPosition(hospitalPin, hospitalLocation)
            myMap.Children.Add(hospitalPin)
            
            Dim resultStackPanel As New StackPanel
            resultStackPanel.Orientation = Orientation.Horizontal
            Dim myStackPanel As New StackPanel
            myStackPanel.Margin = New Thickness(5)
            myStackPanel.Background = New SolidColorBrush(Colors.Navy)
            Dim myNameTextBlock As New TextBlock
            myNameTextBlock.Foreground = New SolidColorBrush(Colors.Red)
            myNameTextBlock.Width = 400
            myNameTextBlock.TextWrapping = TextWrapping.Wrap
            myNameTextBlock.FontSize = 24
            myNameTextBlock.Padding = New Thickness(5)
            myNameTextBlock.Text = no.ToString & ":" & "【病院名】=" & result.Element("name").Value
            Dim addressTextBlock As New TextBlock
            addressTextBlock.Foreground = New SolidColorBrush(Colors.Beige)
            addressTextBlock.Width = 400
            addressTextBlock.TextWrapping = TextWrapping.Wrap
            addressTextBlock.FontSize = 20
            addressTextBlock.Padding = New Thickness(5)
            addressTextBlock.Text = "【住所】=" & result.Element("vicinity").Value
            myHospitalInfo.Add(New HospitalInfo With {.病院名 = no.ToString & ":【病院名】=" & result.Element("name").Value, .住所 = "【住所】=" & result.Element("vicinity").Value})
            Dim myButton As New Button
            With myButton
              .Content = "Webで表示"
              .Tag = no.ToString
            End With
 
            Dim readingAddress As String = no.ToString & "ばんめ、病院めいは" & result.Element("name").Value & "です。住所は" & result.Element("vicinity").Value & "です。詳細はウェブで確認してください。"
          myStackPanel.SetValue(Canvas.ZIndexProperty, no)
          myStackPanel.Children.Add(myNameTextBlock)
          myStackPanel.Children.Add(addressTextBlock)
          myStackPanel.Children.Add(myButton)
          resultStackPanel.Children.Add(myStackPanel)
          resultStackPanel.Children.Add(syokoStackPanel)
          resultStackPanel.Visibility = Xaml.Visibility.Collapsed
          MapLayer.SetPosition(resultStackPanel, New Location(CDbl(myLatitude), CDbl(myLongitude)))
        myMap.Children.Add(resultStackPanel)
        resultStackPanel.SetValue(Canvas.ZIndexProperty, no)
        AddHandler hospitalPin.Tapped, Async Sub()
          resultStackPanel.Visibility = Xaml.Visibility.Visible
          myImage.Visibility = Xaml.Visibility.Visible
          myMedia = Me.MediaElement1
          Dim synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer
          Dim stream = Await synth.SynthesizeTextToStreamAsync(readingAddress)
          myMedia.SetSource(stream, stream.ContentType)
          myMedia.Play()
        End Sub

        AddHandler resultStackPanel.Tapped, Sub()
          myMedia.Stop()
          resultStackPanel.Visibility = Xaml.Visibility.Collapsed
        End Sub
 
        AddHandler myButton.Click, Async Sub(mySender As Object, myArgs As RoutedEventArgs)
          Dim index = DirectCast(mySender, Button).Tag
          Dim myIndex = CInt(index) - 1
          Dim mySendAddress = httpDoc.Descendants("vicinity")(myIndex).Value & " " & httpDoc.Descendants("name")(myIndex).Value
          Await Windows.System.Launcher.LaunchUriAsync(New Uri("http://www.bing.com/search?q=" & Uri.EscapeDataString(mySendAddress), UriKind.Absolute))
        End Sub
      Next
        GridView1.ItemsSource = myHospitalInfo
    Catch
      ErrorShow()
  End Try
        End Sub)
End Sub
  • 近くの病院をキャラクターが音声で教えてくれるアプリ

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

薬師寺国安事務所代表。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メルマガ会員のサービス内容を見る

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