Два метода для загрузки содержимого в ComboBox (или другие элементы управления) без замораживания формы контейнера.
Примечание: список ComboBox не поддерживает бесконечное число элементов. DropDown фактически перестанет работать после добавления в список 65534
элементов.
DropDownList и ListBox могут поддерживать больше элементов, но в некоторый момент они также начинают разрушаться (~80,000
элементов ), прокрутка и рендеринг предметов будут заметно скомпрометированы.
► Первый метод - в огне и забыть стиль. Задача запускает метод, который загружает данные из некоторого источника. Когда загрузка заканчивается, данные устанавливаются как ComboBox.DataSource.
A CancellationTokenSource используется для передачи CancellationToken в метод, чтобы сигнализировать о необходимости отмены при необходимости. Метод возвращается, если обнаруживает, что был вызван CancellationTokenSource.Cancel()
, проверяя (когда это возможно) свойство CancellationToken.IsCancellationRequested
.
CancellationTokenSource.Cancel()
также вызывается при закрытии формы, если загрузка данных еще продолжается.
Установите CancellationTokenSource
на null
(Nothing
) при утилизации: его IsDiposed
свойство является внутренним и не может быть доступно напрямую.
BeginInvoke()
используется для выполнения этой операции в потоке пользовательского интерфейса. Без него будет System.InvalidOperationException с причиной Illegal Cross-thread Operation
.
Перед настройкой источника данных вызывается BeginUpdate()
, чтобы предотвратить перекрашивание ComboBox, пока элементы управления загружают данные. BeginUpdate
обычно вызывается, когда элементы добавляются по одному, чтобы избежать мерцания и улучшить производительность, но это также полезно в этом случае. Это более очевидно во втором методе.
Private cts As CancellationTokenSource
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
cts = New CancellationTokenSource()
Task.Run(Function() GetProducts(Me.ComboBox1, cts.Token))
'Code here is executed right after Task.Run()
End Sub
Public Function GetProducts(ctrl As ComboBox, token As CancellationToken) As Task
If token.IsCancellationRequested Then Return Nothing
' Begin loading data, synchronous or asynchrnonous
' The CancellationToken (token) can be passed to other procedures or
' methods that accept a CancellationToken
' (...)
' End loading data, synchronous or asynchrnonous
If token.IsCancellationRequested Then Return Nothing
ctrl.BeginInvoke(New MethodInvoker(
Sub()
ctrl.BeginUpdate()
ctrl.DataSource = [The DataSource]
ctrl.EndUpdate()
End Sub
))
Return Nothing
End Function
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
If cts IsNot Nothing Then
cts.Cancel()
cts.Dispose()
End If
End Sub
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
If cts IsNot Nothing Then
cts.Cancel()
cts.Dispose()
cts = Nothing
End If
End Sub
![Form asynchronous loading 1](https://i.stack.imgur.com/hBN6m.gif)
► Второй метод использует asyn c / шаблон ожидания
Модификатор Asyn c добавлен в обработчик Form.Shown
.
Оператор Await применяется к Task.Run()
, приостанавливая выполнение другого кода в методе до тех пор, пока задача не вернется, в то время как управление возвращается текущему потоку для других операций.
GetProducts()
- это метод Async
, который возвращает Задачу, в данном случае.
Код, следующий за вызовом Await Task.Run()
, выполняется после возврата GetProducts()
.
Эта процедура работает иначе, чем предыдущая:
здесь предполагается, что данные загружаются в коллекцию - какой-то IEnumerable<T>
- возможно, List<T>
, как показано в вопрос.
Данные, если они доступны, добавляются в коллекцию ComboBox.Items
кусками 120
элементов (не числом magi c, их можно настроить на любое другое значение в связи со сложностью данных) в al oop.
Await Task.Delay () вызывается в начале, чтобы соответствовать требованиям async/await
. В этом нет необходимости, его можно удалить, но появится предупреждение об отсутствующем операторе Await
.
Здесь нет CancellationTokenSource
. Не потому, что в этом шаблоне нет необходимости, просто потому, что я думаю, что было бы неплохо попытаться добавить CancellationToken
к вызову метода, как показано в предыдущем примере, к Знакомство . Поскольку в этом методе используется al oop, к запросу l oop может быть добавлена проверка запроса на отмену, что делает отмену еще более эффективной.
Private Async Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
Await Task.Run(Function() GetProducts(Me.ComboBox1))
' Code here is executed after the GetProducts() method returns
End Sub
Public Async Function GetProducts(ctrl As ComboBox) As Task
Await Task.Delay(10)
' Begin loading data, synchronous or asynchrnonous
' (...)
' Generates [The List] Enumerable object
' End loading data, synchronous or asynchrnonous
Dim position As Integer = 0
For i As Integer = 0 To ([The List].Count \ 120)
ctrl.BeginInvoke(New MethodInvoker(
Sub()
ctrl.BeginUpdate()
ctrl.Items.AddRange([The List].Skip(position).Take(120).ToArray())
ctrl.EndUpdate()
position += 120
End Sub
))
Next
End Function
![Form asynchronous loading 2](https://i.stack.imgur.com/LSboZ.gif)