title: Blocking the UI thread while waiting for network activity
body: <p>I am now maintaining a piece of software written in VB.net (.net 3.5) that communicates over the network with another software running elsewhere which I have no control over.  I am not very familiar with WinForms software or best practices, but here's what I understand about the program:</p>
  
  <p>When certain actions are taken place by the user via the UI, the program sends a message to the host process and waits for a response to come back, handles the results of the response, and then allows the user to continue.  The UI must be locked while this conversation is taking place between the client and host.  This conversation may take up to two or three seconds depending on the amount of stress the host is under.</p>
  
  <p>The writer of this software is blocking the UI thread with while loops while this communication is going on.  Obviously this is not a good approach as it can result in Windows reporting that the program has locked up and giving the option to exit.</p>
  
  <p>My question is, what is the best practice for doing this.  The winforms program has several windows with hundreds of buttons and controls that all must be blocked during the communication.  Communication (and waiting) takes place as the result of hundreds of different possible user interactions with the program.  So, how can I block the whole program from accepting input, button presses, keyboard, etc. while the communication is going on?</p>
  
  <p>Here's some stripped-down code of how it's currently working.  I'm not afraid of significantly rewriting the communications portion of this program.</p>
  
  <pre><code>Private clientSocket As New System.Net.Sockets.TcpClient()
  Private serverStream As NetworkStream
  Private readThread As Thread = New Thread(AddressOf readLoop)
  
  
  
  Public Sub Connect()
      'This function called to start the connection when program starts
      Try
          clientSocket.Connect(_Address, _Port)
          serverStream = clientSocket.GetStream()
  
          readThread.IsBackground = True
          readThreadActive = True
          readThread.Start()
  
  
      Catch ex As Exception
          RaiseEvent communicationsErrorEvent("CONNECT: " &amp; ex.Message.ToString)
      End Try
  End Sub
  
  Public Sub WriterLogOn(ByVal UserName As String, ByVal Password As String)
      'This Sub is called from a button press on a WinForm
  
      Dim xmlString As String = "blah" 'generate xmlstring to send to host including username and encrypted password
  
      communicationsState = state_WriterLogOn 'store the state from an enum
      'each communication type has its own enum
  
      write(xmlString)
  
      While communicationsState = state_WriterLogOn
          Thread.Sleep(1)
      End While
  End Sub
  
  
  Private Sub write(ByVal writeString As String)
      Try
          Dim outStream As Byte() = System.Text.Encoding.ASCII.GetBytes(writeString &amp; Chr(0))
          serverStream.Write(outStream, 0, outStream.Length)
          serverStream.Flush()
      Catch ex As Exception
          RaiseEvent communicationsErrorEvent("WRITE: " &amp; ex.Message)
      End Try
  End Sub
  
  
  Private Sub readLoop()
  
      Try
          While (readThreadActive) 'always true unless changed in certain conditions
  
              Dim buffSize As Integer
              Dim inStream(10024) As Byte
              buffSize = clientSocket.ReceiveBufferSize
              Dim numberOfBytesRead As Integer = 0
              Dim returnDataBuilder As StringBuilder = New StringBuilder
  
              Do
                  numberOfBytesRead = serverStream.Read(inStream, 0, inStream.Length)
                  returnDataBuilder.AppendFormat("{0}", Encoding.ASCII.GetString(inStream, 0, numberOfBytesRead))
              Loop While serverStream.DataAvailable
  
              Dim returnData As String = returnDataBuilder.ToString()
  
  
              If (returnData.Length &gt; 0) Then
                  ParseMessage(returnData)
              End If
  
              Threading.Thread.Sleep(1)
  
          End While
  
      Catch ex As Exception
          RaiseEvent communicationsErrorEvent("READLOOP: " &amp; ex.Message)
      End Try
  
  End Sub
  
  
  Private Sub ParseMessage(ByVal readdata As String)
      'This function parses the string received and does the appropriate things with it
      'And when appropriate it sets the state back to idle, allowing the UI thread to unblock
      communicationsState = state_Idle
  End Sub
  </code></pre>
  
  <p>The read loop must be running at all times, because it also can receive unsolicited messages at any time that must be handled in the background without blocking the UI thread. (This is done from the ParseMessage() sub).</p>
  
  <p>So, I know enough to know that this is a bad way to handle blocking the UI thread.  But after searching around, I haven't found a good solution for stopping all user input application-wide until some other communications received in a different thread releases it.</p>
  
  <p>I have tried inserting code into the various UI methods that checks the state and stops buttons/keyboard/etc from doing anything, but there are so many different places I would need to insert this code, it feels like the wrong way to do it.  I have searched around, but only find questions about how to NOT block the UI thread.</p>
  
  <p>How can this be handled without Threading.Sleep in the UI thread?  Is a modal form the way to handle this?</p>
  
  <p>Thanks in advance!</p>