非同期アプリにおける再入の処理 (Visual Basic)

非同期コードをアプリに含める場合は、再入を考慮し、場合によっては回避することをお勧めします。これは、完了前に非同期操作の再入力を参照します。 再入の可能性を特定して処理しないと、予期しない結果が発生する可能性があります。

注意

この例を実行するには、Visual Studio 2012 以降と .NET Framework 4.5 以降が、コンピューターにインストールされている必要があります。

注意

現在、アプリ開発で使用する最小バージョンは、トランスポート層セキュリティ (TLS) バージョン 1.2 です。 ご利用のアプリがバージョン 4.7 より前の .NET Framework を対象としている場合は、「.NET Framework でのトランスポート層セキュリティ (TLS) のベスト プラクティス」の記事を参照してください。

再入を認識する

このトピックの例では、ユーザーが [Start] をクリックして非同期アプリを開始します。このアプリは、一連の Web サイトをダウンロードし、ダウンロードされた合計バイト数を計算します。 同期バージョンの例では、ユーザーが何回ボタンをクリックしても同じように応答します。2 回目以降はアプリが実行を完了するまで、UI スレッドはこれらのイベントを無視するからです。 ただし、非同期アプリでは、UI スレッドは応答し続けるので、完了前に非同期操作を再入力することがあります。

次の例は、ユーザーが 1 度だけ [Start] をクリックした場合の出力を示しています。 ダウンロードされた Web サイトの一覧には、各サイトのサイズがバイト単位で表示されます。 合計バイト数は最後に表示されます。

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

ただし、ユーザーがボタンを複数回クリックすると、イベント ハンドラーは繰り返し呼び出され、ダウンロード プロセスはそのたびに再入力されます。 その結果、複数の非同期操作が同時に実行され、出力は結果をインターリーブするので、合計バイト数がややこしくなります。

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
7. msdn.microsoft.com                                            42972
4. msdn.microsoft.com/library/hh290140.aspx               117152
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
7. msdn.microsoft.com                                            42972
5. msdn.microsoft.com/library/hh524395.aspx                68959
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

このトピックの最後にスクロールすると、この出力を生成するコードをレビューできます。 コードを試してみるには、ソリューションをローカル コンピューターにダウンロードにし、WebsiteDownload プロジェクトを実行するか、このトピックの最後にあるコードを使用して独自のプロジェクトを作成します。詳細と手順については、「例のアプリをレビューして実行する」を参照してください。

再入を処理する

再入の処理は、アプリで何を行うかに応じてさまざまな方法で実行できます。 このトピックでは、次の例を紹介します。

[Start] ボタンを無効にする

処理の実行中に [Start] ボタンを利用できないようにするには、StartButton_Click イベント ハンドラーの上部にあるボタンを無効にします。 処理が完了しユーザーが再度アプリを実行できるようになったら、Finally ブロック内からこのボタンを再度有効にできます。

次のコードはこの変更を示しています。変更の部分にはアスタリスクが付いています。 この変更をこのトピックの最後にあるコードに追加できます。また、完成したアプリを「Async Samples: Reentrancy in .NET Desktop Apps」 (非同期の例: .NET デスクトップ アプリでの再入) からダウンロードします。 プロジェクト名は DisableStartButton です。

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' This line is commented out to make the results clearer in the output.
    'ResultsTextBox.Text = ""

    ' ***Disable the Start button until the downloads are complete.
    StartButton.IsEnabled = False

    Try
        Await AccessTheWebAsync()

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    ' ***Enable the Start button in case you want to run the program again.
    Finally
        StartButton.IsEnabled = True

    End Try
End Sub

変更の結果、AccessTheWebAsync が Web サイトをダウンロードしている間は、ボタンが応答しないので、プロセスを再入力できません。

操作を取り消して再開する

[Start] ボタンを無効にせず、有効の状態を保持できますが、ユーザーがボタンを再度クリックしたときに、実行中の処理を取り消し、最後に開始された処理を続行できます。

取り消しの詳細については、「非同期アプリケーションの微調整 (Visual Basic)」を参照してください。

このシナリオを設定するには、「例のアプリをレビューして実行する」に用意されている基本コードを次のように変更します。 また、完成したアプリを「Async Samples: Reentrancy in .NET Desktop Apps」 (非同期の例: .NET デスクトップ アプリでの再入) からダウンロードします。 このプロジェクトの名前は CancelAndRestart です。

  1. すべてのメソッドのスコープである CancellationTokenSource 変数、cts を宣言します。

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
  2. StartButton_Click で、処理が既に実行されているかどうかを確認します。 cts の値が Nothing の場合、アクティブな操作はまだありません。 値が Nothing 以外の場合は、実行中の処理が取り消されます。

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
  3. cts に、現在のプロセスを表す別の値を設定します。

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
  4. StartButton_Click の最後で現在のプロセスが完了します。したがって、cts の値を Nothing に戻します。

    ' *** When the process completes, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
    

次のコードは、StartButton_Click のすべての変更を示しています。 追加部分はアスタリスクが付いています。

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

    ' This line is commented out to make the results clearer.
    'ResultsTextBox.Text = ""

    ' *** If a download process is underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS

    Try
        ' *** Send a token to carry the message if the operation is canceled.
        Await AccessTheWebAsync(cts.Token)

    Catch ex As OperationCanceledException
        ResultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    End Try

    ' *** When the process is complete, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
End Sub

AccessTheWebAsync で、次の変更を行います。

  • StartButton_Click からキャンセル トークンを受け取るためのパラメーターを追加します。

  • GetAsync メソッドを使用して Web サイトをダウンロードします。これは GetAsyncCancellationToken 引数を受け取るからです。

  • DisplayResults を呼び出してダウンロードした各 Web サイトの結果を表示する前に、ct で、現在の処理が取り消されていないことを確認します。

次のコードはこの変更を示しています。変更の部分にはアスタリスクが付いています。

' *** Provide a parameter for the CancellationToken from StartButton_Click.
Private Async Function AccessTheWebAsync(ct As CancellationToken) As Task

    ' Declare an HttpClient object.
    Dim client = New HttpClient()

    ' Make a list of web addresses.
    Dim urlList As List(Of String) = SetUpURLList()

    Dim total = 0
    Dim position = 0

    For Each url In urlList
        ' *** Use the HttpClient.GetAsync method because it accepts a
        ' cancellation token.
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

        ' *** Retrieve the website contents from the HttpResponseMessage.
        Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()

        ' *** Check for cancellations before displaying information about the
        ' latest site.
        ct.ThrowIfCancellationRequested()

        position += 1
        DisplayResults(url, urlContents, position)

        ' Update the total.
        total += urlContents.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function

このアプリの実行中に複数回 [Start] ボタンをクリックすると、次の出力のような結果が生成されます。

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               122505
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

部分的なリストを削除するには、StartButton_Click コードの先頭行のコメントを解除して、ユーザーが操作を再開するたびに、テキスト ボックスをクリアします。

複数の操作を実行して出力をキューに登録する

この 3 番目の例は、ユーザーが [Start] ボタンをクリックするたびに非同期操作が開始され、すべての操作が完了まで実行されるという点で最も複雑です。 要求されたすべての操作によって Web サイトがリストから非同期的にダウンロードされますが、操作からの出力は順次表示されます。 つまり、「再入を認識する」の出力に示されているように、実際のダウンロード アクティビティはインターリーブされますが、各グループの結果のリストは個別に表示されます。

操作は、表示プロセスのゲートキーパーとして機能するグローバル TaskpendingWork を共有します。

この例を実行するには、変更を「アプリケーションをビルドする」のコードに貼り付けます。また、「アプリをダウンロードする」の手順に従って、サンプルをダウンロードし、QueueResults プロジェクトを実行することもできます。

次の出力は、ユーザーが 1 度だけ [Start] ボタンをクリックした場合の結果を示しています。 文字ラベル A は、 [Start] ボタンが最初にクリックされた結果であることを示しています。 数字は、ダウンロード対象の一覧における URL の順序を示しています。

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               209858
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71260
A-6. msdn.microsoft.com/library/ms404677.aspx               199186
A-7. msdn.microsoft.com                                            53266
A-8. msdn.microsoft.com/library/ff730837.aspx               148020

TOTAL bytes returned:  918876

#Group A is complete.

ユーザーが [Start] ボタンを 3 回クリックすると、アプリでは次のような出力が生成されます。 先頭にシャープ記号 (#) が付いている情報行は、アプリケーションの進行状況を追跡します。

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               207089
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71259
A-6. msdn.microsoft.com/library/ms404677.aspx               199185

#Starting group B.
#Task assigned for group B.

A-7. msdn.microsoft.com                                            53266

#Starting group C.
#Task assigned for group C.

A-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916095

B-1. msdn.microsoft.com/library/hh191443.aspx                87389
B-2. msdn.microsoft.com/library/aa578028.aspx               207089
B-3. msdn.microsoft.com/library/jj155761.aspx                30870
B-4. msdn.microsoft.com/library/hh290140.aspx               119027
B-5. msdn.microsoft.com/library/hh524395.aspx                71260
B-6. msdn.microsoft.com/library/ms404677.aspx               199186

#Group A is complete.

B-7. msdn.microsoft.com                                            53266
B-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916097

C-1. msdn.microsoft.com/library/hh191443.aspx                87389
C-2. msdn.microsoft.com/library/aa578028.aspx               207089

#Group B is complete.

C-3. msdn.microsoft.com/library/jj155761.aspx                30870
C-4. msdn.microsoft.com/library/hh290140.aspx               119027
C-5. msdn.microsoft.com/library/hh524395.aspx                72765
C-6. msdn.microsoft.com/library/ms404677.aspx               199186
C-7. msdn.microsoft.com                                            56190
C-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  920526

#Group C is complete.

グループ B とグループ C は、グループ A が終了する前に開始します。ただし、各グループの出力は個別に表示されます。 グループ A のすべての出力が最初に表示され、その後にグループ B のすべての出力が続き、さらにグループ C のすべての出力が表示されます。グループは必ず順番に表示され、グループごとに、個別の Web サイトに関する情報が URL 一覧の URL の順番で表示されます。

ただし、ダウンロードが実際に行われる順序は予測できません。 複数のグループが開始されたら、そのグループが生成するダウンロード タスクはすべてのアクティブです。 B-1 の前に A-1 がダウンロードされる、また、A-2 の前に A-1 がダウンロードされると仮定することはできません。

グローバル定義

サンプル コードには、すべてのメソッドから参照できる次の 2 つのグローバル宣言が含まれます。

Class MainWindow    ' Class MainPage in Windows Store app.

    ' ***Declare the following variables where all methods can access them.
    Private pendingWork As Task = Nothing
    Private group As Char = ChrW(AscW("A") - 1)

Task 変数、pendingWork は、表示プロセスを監視し、あるグループが別のグループの表示操作を中断しないようにします。 文字変数 group は、異なるグループからの出力にラベルを付け、予想された順序で結果が表示されていることを確認します。

Click イベント ハンドラー

イベント ハンドラー StartButton_Click は、ユーザーが [Start] ボタンを選択するたびに、グループ文字をインクリメントします。 ハンドラーは AccessTheWebAsync を呼び出して、ダウンロード操作を実行します。

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' ***Verify that each group's results are displayed together, and that
    ' the groups display in order, by marking each group with a letter.
    group = ChrW(AscW(group) + 1)
    ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Starting group {0}.", group)

    Try
        ' *** Pass the group value to AccessTheWebAsync.
        Dim finishedGroup As Char = Await AccessTheWebAsync(group)

        ' The following line verifies a successful return from the download and
        ' display procedures.
        ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Group {0} is complete." & vbCrLf, finishedGroup)

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."

    End Try
End Sub

AccessTheWebAsync メソッド

この例では、AccessTheWebAsync を 2 つのメソッドに分割します。 最初のメソッド、AccessTheWebAsync は、グループのすべてのダウンロード タスクを開始し、pendingWork を設定して表示プロセスを制御します。 このメソッドは、統合言語クエリ (LINQ クエリ) と ToArray を使用して、すべてのダウンロードを同時に開始します。

その後、AccessTheWebAsync は、FinishOneGroupAsync を呼び出して各ダウンロードが完了するまで待機し、その長さを表示します。

FinishOneGroupAsync は、pendingWorkAccessTheWebAsync に割り当てられたタスクを返します。 この値は、タスクが完了する前に、別の操作によって操作が中断されないようにします。

Private Async Function AccessTheWebAsync(grp As Char) As Task(Of Char)

    Dim client = New HttpClient()

    ' Make a list of the web addresses to download.
    Dim urlList As List(Of String) = SetUpURLList()

    ' ***Kick off the downloads. The application of ToArray activates all the download tasks.
    Dim getContentTasks As Task(Of Byte())() =
        urlList.Select(Function(addr) client.GetByteArrayAsync(addr)).ToArray()

    ' ***Call the method that awaits the downloads and displays the results.
    ' Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp)

    ResultsTextBox.Text &=
        String.Format(vbCrLf & "#Task assigned for group {0}. Download tasks are active." & vbCrLf, grp)

    ' ***This task is complete when a group has finished downloading and displaying.
    Await pendingWork

    ' You can do other work here or just return.
    Return grp
End Function

FinishOneGroupAsync メソッド

このメソッドは、グループのダウンロード タスクを循環参照し、それぞれを待機してから、ダウンロードされた Web サイトの長さを表示して、その長さを合計に追加します。

FinishOneGroupAsync の最初のステートメントは、pendingWork を使用して、メソッドを入力することが、表示プロセスの操作または待機中の操作の妨げにならないようにします。 これらの操作が進行中の場合、入力操作は順番を待つ必要があります。

Private Async Function FinishOneGroupAsync(urls As List(Of String), contentTasks As Task(Of Byte())(), grp As Char) As Task

    ' Wait for the previous group to finish displaying results.
    If pendingWork IsNot Nothing Then
        Await pendingWork
    End If

    Dim total = 0

    ' contentTasks is the array of Tasks that was created in AccessTheWebAsync.
    For i As Integer = 0 To contentTasks.Length - 1
        ' Await the download of a particular URL, and then display the URL and
        ' its length.
        Dim content As Byte() = Await contentTasks(i)
        DisplayResults(urls(i), content, i, grp)
        total += content.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function

この例を実行するには、変更を「アプリケーションをビルドする」のコードに貼り付けます。また、「アプリをダウンロードする」の手順に従って、サンプルをダウンロードし、QueueResults プロジェクトを実行することもできます。

目的のポイント

出力で先頭にシャープ記号 (#) が付いている情報行は、この例の動作を明確に示しています。

出力のパターンを次に示します。

  • 前のグループが出力を表示していても、グループを開始できます。その際、前のグループの出力の表示は中断されません。

    #Starting group A.
    #Task assigned for group A. Download tasks are active.
    
    A-1. msdn.microsoft.com/library/hh191443.aspx                87389
    A-2. msdn.microsoft.com/library/aa578028.aspx               207089
    A-3. msdn.microsoft.com/library/jj155761.aspx                30870
    A-4. msdn.microsoft.com/library/hh290140.aspx               119037
    A-5. msdn.microsoft.com/library/hh524395.aspx                71260
    
    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    
    A-6. msdn.microsoft.com/library/ms404677.aspx               199186
    A-7. msdn.microsoft.com                                            53078
    A-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915919
    
    B-1. msdn.microsoft.com/library/hh191443.aspx                87388
    B-2. msdn.microsoft.com/library/aa578028.aspx               207089
    B-3. msdn.microsoft.com/library/jj155761.aspx                30870
    
    #Group A is complete.
    
    B-4. msdn.microsoft.com/library/hh290140.aspx               119027
    B-5. msdn.microsoft.com/library/hh524395.aspx                71260
    B-6. msdn.microsoft.com/library/ms404677.aspx               199186
    B-7. msdn.microsoft.com                                            53078
    B-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915908
    
  • 最初に開始したグループ A についてのみ、FinishOneGroupAsync 開始時の、pendingWork タスクは Nothing になります。 FinishOneGroupAsync に達したとき、グループ A はまだ await 式を完了していません。 したがって、コントロールは AccessTheWebAsync に戻っておらず、pendingWork への最初の割り当ては発生していません。

  • 次の 2 行は、出力に必ず同時に表示されます。 StartButton_Click のグループ操作が開始してから、グループのタスクが pendingWork に割り当てられるまでの間、コードが中断されることは決してありません。

    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    

    グループが StartButton_Click に移行したら、FinishOneGroupAsync に移行するまでは、await 式は完了しません。 したがって、コード セグメントの途中で、他の操作がコントロールを得ることはありません。

例のアプリをレビューして実行する

サンプル アプリをさらに詳しく理解するには、そのアプリをダウンロードし、ご自身でビルドしてみてください。また、このトピックの最後にあるコードをレビューすることもできます。アプリを実装する必要はありません。

注意

Windows Presentation Foundation (WPF) デスクトップ アプリとして例を実行するには、Visual Studio 2012 以降と .NET Framework 4.5 以降がコンピューターにインストールされている必要があります。

アプリをダウンロードする

  1. 圧縮ファイルを「Async Samples: Reentrancy in .NET Desktop Apps」 (非同期の例: .NET デスクトップ アプリでの再入) からダウンロードします。

  2. ダウンロードしたファイルを圧縮解除し、Visual Studio を起動します。

  3. メニュー バーで [ファイル][開く][プロジェクト/ソリューション] の順に選択します。

  4. 圧縮解除したサンプル コードが含まれるフォルダーに移動し、ソリューション (.sln) ファイルを開きます。

  5. ソリューション エクスプローラーで、実行するプロジェクトのショートカット メニューを開き、 [スタートアップ プロジェクトに設定] を選択します。

  6. Ctrl キーを押しながら F5 キーを押してプロジェクトをビルドし、実行します。

アプリケーションをビルドする

次のセクションでは、WPF アプリとして例をビルドするコードを示します。

WPF アプリをビルドするには
  1. Visual Studio を起動します。

  2. メニュー バーで、 [ファイル][新規作成][プロジェクト] の順にクリックします。

    [新しいプロジェクト] ダイアログ ボックスが表示されます。

  3. [インストールされたテンプレート] ペインで、 [Visual Basic] を展開し、 [Windows] を展開します。

  4. プロジェクトの種類の一覧の [WPF アプリケーション] をクリックします。

  5. プロジェクトに WebsiteDownloadWPF という名前を指定し、4.6 以降の .NET Framework のバージョンを選択して、[OK] ボタンをクリックします。

    ソリューション エクスプローラーに新しいプロジェクトが表示されます。

  6. Visual Studio コード エディターで、 [MainWindow.xaml] タブをクリックします。

    タブが表示されない場合は、ソリューション エクスプローラーで MainWindow.xaml のショートカット メニューを開き、 [コードの表示] を選択します。

  7. MainWindow.xaml の XAML ビューで、コードを次のコードに置き換えます。

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    

    テキスト ボックスとボタンを含む簡単なウィンドウが、MainWindow.xaml のデザイン ビューに表示されます。

  8. ソリューション エクスプローラー[参照] を右クリックし、 [参照の追加] を選択します。

    まだ選択されていない場合は、System.Net.Http の参照を追加します。

  9. ソリューション エクスプローラーで MainWindow.xaml.vb のショートカット メニューを開き、 [コードの表示] を選択します。

  10. MainWindow.xaml.vb のコードを次のコードに置き換えます。

    ' Add the following Imports statements, and add a reference for System.Net.Http.
    Imports System.Net.Http
    Imports System.Threading
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol Or System.Net.SecurityProtocolType.Tls12
    
            ' This line is commented out to make the results clearer in the output.
            'ResultsTextBox.Text = ""
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    
            End Try
        End Sub
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object.
            Dim client = New HttpClient()
    
            ' Make a list of web addresses.
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task
                ' produces a byte array.
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/hh191443.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/jj155761.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/hh524395.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
            Return urls
        End Function
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' Display the length of each website. The string format is designed
            ' to be used with a monospaced font, such as Lucida Console or
            ' Global Monospace.
    
            ' Strip off the "http:'".
            Dim displayURL = url.Replace("https://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        End Sub
    End Class
    
  11. Ctrl キーを押しながら F5 キーを押してプログラムを実行し、 [Start] ボタンを複数回クリックします。

  12. [Start] ボタンを無効にする」、「操作を取り消して再開する」、または「複数の操作を実行して出力をキューに登録する」の変更を行って再入を処理します。

関連項目