How to fill a ListBox using multi thread in VB .NET?

0

In a Windows Form Application I have a List Box that is populated from a list of IP's. It works like this: there is a text file ( config.eep ) that contains a series of IP's, at the press of a button to update the List Box, a loop is called by pinging all the IP's of the file. Only respondents are entered in ListBox .

This is the code that updates ListBox ( lstListaIpAtiv ):

Public Sub AtualizarListBox()
    'Atualiza a ListBox
    lstListaIpAtiv.Items.Clear()                                     'Limpa a ListBox
    Dim ping As Ping = New Ping
    Dim sLinhaAtual As String                                        'Linha corrente do arquivo de configuração
    Dim PingReply As PingReply
    Dim objLeitor As StreamReader
    Try
        objLeitor = File.OpenText(sCaminho)
    Catch ex As FileNotFoundException
        MsgBox("Arquivo de configuração não encontrado", MsgBoxStyle.OkOnly, "Aviso")
        Exit Sub
    End Try
    Me.Cursor = Cursors.WaitCursor                                     'Troca o cursor para o cursor de espera
    While objLeitor.Peek <> -1                                       'Looping nas Linhas, enquanto nao chegar ao fim do arquivo texto, ele não sai do loop
        sLinhaAtual = objLeitor.ReadLine()                           'Recupera a Linha do Arquivo e guarda na variavel do tipo string sLinhaAtual
        Try
            PingReply = ping.Send(sLinhaAtual)                       'Envia requisiçao de ping para IP da linha corrente
            If PingReply.Status = IPStatus.Success Then              'Verifica se houve resposta do IP em questão
                lstListaIpAtiv.Items.Add(sLinhaAtual)                'Grava a linha atual na list box caso essa responda ao Ping
            End If
        Catch ex As PingException
            Continue While
        End Try
    End While
    objLeitor.Close()                                                'Fecha o arquivo para escrita
    If (Not objLeitor Is Nothing) Then
        objLeitor.Dispose()
    End If
    Me.Cursor = Cursors.Arrow                                       'Retorna o cursor para o cursor normal
    lblNumMaqEnc.Text = lstListaIpAtiv.Items.Count()                'Conta quantos itens existem na ListBox e exibe na label
End Sub

The problem is that while this process does not end, the rest of the application is unavailable. I am trying to resolve this by creating another thread to run this separate process while the primary thread continues with running the rest of the application. I inserted the following code in the button that updates the ListBox ( btnAtualizar ):

 Dim AtualizaThread As New Thread(AddressOf AtualizarListBox)
 AtualizaThread.Start()

However, I get the following error:

  

Cross-thread operation not valid: Control 'lstListaIpAtiv' accessed from a thread other than the thread it was created on.

How to get around this?

    
asked by anonymous 31.01.2014 / 19:42

1 answer

3

Interface changes can only be made by the thread itself that runs the interface.

The easiest way to ensure this happens is to put the following code at the beginning of your method:

Public Sub AtualizarListBox()
  If Me.InvokeRequired Then 
    Me.Invoke(New MethodInvoker(AddressOf AtualizarListBox)) 
  Else 
    ' seu código aqui
  End If 
End Sub

With this code, you will no longer be changing the ListBox directly, but instead passing a delegate to the form, asking it to run its code on its thread when it can.

However, if your code is slow, it will crash the interface thread until the upgrade completes, and the advantage of being a separate thread will undo itself. In this case, you have two paths to solve:

1. Separate a smaller method just to make the updates in the interface

For example, it separates a method just to add an individual item in the ListBox, once it has already been pinged and has the result in hand.

2. Do not use threads

You do not necessarily need to use threads. Simply put, within your loop, a call to Application.DoEvents() . So you're going to release the UI thread to run the pending events (refresh the screen, process clicks, etc.), and so it eliminates the feeling that the application "froze".

Remembering that a mix of the two alternatives can also be done ... You can use threads, keeping your algorithm as it is, only using the Invoke as I put it at the beginning of the response, and adding a Application.DoEvents() in the middle of the your loop so the UI does not freeze.

    
31.01.2014 / 19:47