2013-05-01 63 views
1

我現在正在維護一個用VB.net(.net 3.5)編寫的軟件,它通過網絡與另一個我無法控制的其他軟件進行通信。我對WinForms軟件或最佳實踐不是很熟悉,但是我對此程序的理解如下:在等待網絡活動時阻塞UI線程

當用戶通過UI執行某些操作時,程序會向主機進程發送消息並等待回覆的響應,處理響應的結果,然後允許用戶繼續。在客戶端和主機之間進行對話時,UI必須被鎖定。這個對話可能需要兩到三秒鐘,這取決於主持人的壓力。

這個軟件的作者在這個通信正在進行時用while循環來阻塞UI線程。顯然,這不是一個好方法,因爲它可能會導致Windows報告程序已鎖定並提供退出選項。

我的問題是,這樣做的最佳做法是什麼。 Winforms程序有幾個窗口,上面有數百個按鈕和控件,在通訊過程中所有按鈕都必須被阻止。通信(和等待)是由於數百種不同的可能的用戶與程序的交互而發生的。那麼,在通訊過程中,我怎樣才能阻止整個程序接受輸入,按鍵,鍵盤等?

下面是一些關於它當前工作的精簡代碼。我不害怕重寫這個程序的通信部分。

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: " & 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 & Chr(0)) 
     serverStream.Write(outStream, 0, outStream.Length) 
     serverStream.Flush() 
    Catch ex As Exception 
     RaiseEvent communicationsErrorEvent("WRITE: " & 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 > 0) Then 
       ParseMessage(returnData) 
      End If 

      Threading.Thread.Sleep(1) 

     End While 

    Catch ex As Exception 
     RaiseEvent communicationsErrorEvent("READLOOP: " & 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 

讀取循環必須在任何時候運行,因爲它也可以在必須在後臺,而不會阻塞UI線程處理的任何時間收到請自來的郵件。 (這是從ParseMessage()子)完成的。

所以,我足夠清楚知道這是處理阻塞UI線程的不好方法。但是在四處搜索之後,我還沒有找到一個很好的解決方案來停止所有用戶輸入的應用程序範圍,直到其他線程收到的其他通信釋放它爲止。

我已經嘗試將代碼插入各種UI方法,檢查狀態並停止按鈕/鍵盤/等做任何事情,但有很多不同的地方我需要插入此代碼,感覺就像錯誤的方式去做吧。我已經搜索過,但只發現有關如何不阻止UI線程的問題。

如何在不使用線程處理的情況下處理這個問題?模式是如何處理這個問題的?

在此先感謝!

+0

最佳做法是*從不*阻止UI線程。如果您需要阻止用戶採取措施,請禁用用戶界面的交互部分,直到用戶再次使用它們的安全爲止。禁用UI的一個選項是啓動一個模式「進度」對話框(最後提到),但它不是唯一的選擇。 – dlev 2013-05-01 21:14:54

+0

BackgroudWorker with ReportsProgress – Paparazzi 2013-05-01 21:21:29

回答

0

如何禁用窗體上的所有控件,然後顯示用戶反饋以聲明長時間運行的操作正在進行 - 可能是永無止境的進度條?您可以使用下面的功能,只需從需要的地方調用即可。

Public Sub DisableUI(frm as form) 

For Each c as Control in frm.controls 

'determine if control is already disabled and set a flag for later 

If c.Enabled = False Then 

c.Tag = "1" 

Else 

c.Enabled = False 

End If 

Next 

End Sub 

Public Sub EnabledUI(frm as form) 

For Each c as Control in frm.controls 

If c.Tag.toString = "1" Then 

'Control should stay disabled 

Else 

c.Enabled = True 

End If 

Next 

End Sub