Поделиться через


Обработка повторного входа в асинхронных приложениях (Visual Basic)

При включении асинхронного кода в приложение следует рассмотреть и, возможно, предотвратить повторение, которое относится к повторному вводу асинхронной операции перед его завершением. Если вы не определяете и обрабатываете возможности повторного входа, это может привести к непредвиденным результатам.

Замечание

Чтобы запустить пример, необходимо установить Visual Studio 2012 или более поздней версии и .NET Framework 4.5 или более поздней версии на компьютере.

Замечание

Протокол TLS версии 1.2 теперь является минимальной версией, используемой в разработке приложений. Если приложение предназначено для версии .NET Framework более ранней версии 4.7, ознакомьтесь со следующей статьей по рекомендациям по протоколу TLS с помощью .NET Framework.

Распознавание повторного входа

В примере в этом разделе пользователи выбирают кнопку "Пуск ", чтобы инициировать асинхронное приложение, которое скачивает ряд веб-сайтов и вычисляет общее количество скачанных байтов. Синхронная версия примера будет отвечать одинаково независимо от того, сколько раз пользователь выбирает кнопку, так как после первого раза поток пользовательского интерфейса игнорирует эти события, пока приложение не завершит работу. Однако в асинхронном приложении поток пользовательского интерфейса продолжает реагировать, и вы можете повторно ввести асинхронную операцию до завершения.

В следующем примере показаны ожидаемые выходные данные, если пользователь нажимает кнопку "Пуск " только один раз. Список загруженных веб-сайтов отображается с размером в байтах каждого сайта. Общее число байтов отображается в конце.

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 или с помощью кода в конце этого раздела, чтобы создать собственный проект. Дополнительные сведения и инструкции см. в разделе "Просмотр и запуск примера приложения".

Обработка повторного входа

Можно обрабатывать повторный вход различными способами в зависимости от того, что вы хотите, чтобы ваше приложение делало. В этом разделе приведены следующие примеры:

  • Отключение кнопки "Пуск"

    Отключите кнопку "Пуск " во время выполнения операции, чтобы пользователь не смог прервать его.

  • Отмена и перезапуск операции

    Отмените любую операцию, которая по-прежнему выполняется, когда пользователь снова нажимает кнопку "Пуск ", а затем позволить последней запрошенной операции продолжить.

  • Запустите несколько операций и поставьте результат в очередь

    Разрешить асинхронному выполнению всех запрошенных операций, но координировать отображение выходных данных таким образом, чтобы результаты каждой операции отображались вместе и в порядке.

Отключение кнопки "Пуск"

Вы можете заблокировать кнопку "Пуск " во время выполнения операции, отключив кнопку в верхней части обработчика StartButton_Click событий. После завершения операции можно повторно включить кнопку из Finally блока, чтобы пользователи могли снова запустить приложение.

В следующем коде показаны эти изменения, помеченные звездочками. Вы можете добавить изменения в код в конце этого раздела или скачать готовое приложение из Async Samples: Reentrancy in .NET Desktop Apps. Имя проекта — 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 загрузки веб-сайтов, поэтому процесс невозможно повторно запустить.

Отмена и перезапуск операции

Вместо отключения кнопки "Пуск " можно сохранить активную кнопку, но если пользователь снова выберет эту кнопку, отмените операцию, которая уже запущена и позволить последней запущенной операции продолжить.

Дополнительные сведения об отмене см. в разделе Fine-Tuning Асинхронное приложение (Visual Basic).

Чтобы настроить этот сценарий, внесите следующие изменения в базовый код, предоставленный в разделе "Просмотр и запуск примера приложения". Вы также можете скачать готовое приложение из Async Samples: Reentrancy in .NET Desktop Apps. Имя этого проекта — 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 для скачивания веб-сайтов, поскольку GetAsync принимает аргумент CancellationToken.

  • Перед вызовом DisplayResults для отображения результатов для каждого скаченного веб-сайта проверьте 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

При нажатии кнопки "Пуск " несколько раз во время работы этого приложения результаты должны быть похожи на следующие выходные данные:

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, чтобы очистить текстовое поле каждый раз, когда пользователь перезапускает операцию.

Выполнение нескольких операций и очередь выходных данных

Этот третий пример является самым сложным в том, что приложение запускает другую асинхронную операцию каждый раз, когда пользователь нажимает кнопку "Пуск " и все операции выполняются до завершения. Все запрошенные операции загружают веб-сайты из списка асинхронно, но выходные данные из операций представлены последовательно. То есть фактическое действие скачивания переключается, как показано в выходных данных в разделе "Распознавание повторного входа", но список результатов для каждой группы представлен отдельно.

Операции совместно используют глобальный Taskобъект, pendingWorkкоторый служит контролёром для процесса отображения.

Этот пример можно запустить, вставив изменения в код в сборке приложения, или следуйте инструкциям в разделе "Скачивание приложения ", чтобы скачать пример, а затем запустить проект QueueResults.

В следующих выходных данных отображается результат, если пользователь нажимает кнопку "Пуск " только один раз. Метка буквы A указывает, что результат будет получен при первом выборе кнопки "Пуск ". Числа показывают порядок 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.

Если пользователь нажимает кнопку "Пуск " три раза, приложение создает выходные данные, похожие на следующие строки. Строки информации, начинающиеся с знака фунта (#) трассировки хода выполнения приложения.

#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, а затем все выходные данные для группы B, а затем все выходные данные для группы C. Приложение всегда отображает группы по порядку и для каждой группы всегда отображает сведения о отдельных веб-сайтах в том порядке, в котором URL-адреса отображаются в списке URL-адресов.

Однако вы не можете предсказать порядок, в котором на самом деле происходят скачивание. После запуска нескольких групп задачи загрузки, создаваемые ими, являются активными. Вы не можете предусмотреть, что A-1 будет загружен перед B-1, и что A-1 будет загружен перед A-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увеличивает букву группы при каждом нажатии кнопки "Пуск ". Затем обработчик вызывает 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 на два метода. Первый метод, AccessTheWebAsyncзапускает все задачи загрузки для группы и настраивает pendingWork для управления процессом отображения. В методе используется языковой интегрированный запрос (запрос LINQ) и ToArray для запуска всех задач загрузки одновременно.

AccessTheWebAsync затем вызывает FinishOneGroupAsync, чтобы дождаться завершения каждой загрузки и отобразить её длину.

FinishOneGroupAsync возвращает задачу, назначенную pendingWork в AccessTheWebAsync. Это значение предотвращает прерывание другой операции до завершения задачи.

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

Этот метод перебирает задачи скачивания в группе, ожидая завершения каждой из них, отображая размер загруженного веб-сайта и добавляя этот размер к общему.

Первая инструкция в 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
    
  • Задача pendingWork предназначена Nothing только в начале FinishOneGroupAsync для группы А, начавшей первой. Группа A еще не завершила выражение ожидания при достижении FinishOneGroupAsync. Поэтому управление не вернулось к AccessTheWebAsync, и первое присвоение pendingWork не произошло.

  • Следующие две строки всегда отображаются вместе в выходных данных. Код никогда не прерывается между запуском операции группы в StartButton_Click и назначением задачи этой группе в pendingWork.

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

    После того как группа войдёт в StartButton_Click, операция не завершает выражение ожидания до тех пор, пока операция не войдёт в FinishOneGroupAsync. Поэтому никакие другие операции не могут контролировать этот сегмент кода.

Просмотр и запуск примера приложения

Чтобы лучше понять пример приложения, вы можете скачать его, создать его самостоятельно или просмотреть код в конце этого раздела без реализации приложения.

Замечание

Чтобы запустить пример в классическом приложении Windows Presentation Foundation (WPF), необходимо установить Visual Studio 2012 или более поздней версии и .NET Framework 4.5 или более поздней версии на компьютере.

Скачивание приложения

  1. Скачайте сжатый файл из Async Samples: Реентерабельность в классических приложениях .NET.

  2. Распакуйте скачанный файл и запустите Visual Studio.

  3. В строке меню выберите "Файл", "Открыть", "Проект или решение".

  4. Перейдите в папку, содержащую декомжатый пример кода, а затем откройте файл решения (.sln).

  5. В обозревателе решений откройте контекстное меню для проекта, который требуется запустить, а затем выберите "Задать как StartUpProject".

  6. Выберите клавиши CTRL+F5 для сборки и запуска проекта.

Создание приложения

В следующем разделе приведен код для создания примера в виде приложения WPF.

Создание приложения WPF
  1. Запустите Visual Studio.

  2. В строке меню выберите "Файл", "Создать", "Проект".

    Откроется диалоговое окно "Новый проект ".

  3. В области установленных шаблонов разверните Visual Basic и разверните Windows.

  4. В списке типов проектов выберите приложение WPF.

  5. Присвойте проекту WebsiteDownloadWPFимя, выберите версию .NET Framework версии 4.6 или более поздней, а затем нажмите кнопку "ОК ".

    Новый проект появится в обозревателе решений.

  6. В редакторе Visual Studio Code выберите вкладку MainWindow.xaml .

    Если вкладка не отображается, откройте контекстное меню mainWindow.xaml в обозревателе решений и выберите команду View Code.

  7. В представлении XAML MainWindow.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 для запуска программы, а затем нажмите кнопку "Пуск " несколько раз.

  12. Внесите изменения в надписи «Отключить кнопку "Пуск"», «Отмена и перезапуск операции» или «Выполнить несколько операций и постановка заданий в очередь», чтобы реализовать повторный вход.

См. также