1

我现在正在维护一个用 VB.net (.net 3.5) 编写的软件,它通过网络与在其他地方运行的另一个我无法控制的软件进行通信。我对 WinForms 软件或最佳实践不是很熟悉,但以下是我对该程序的了解:

当用户通过 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 线程的问题。

如果没有 UI 线程中的 Threading.Sleep,如何处理?模态形式是处理这个问题的方法吗?

提前致谢!

4

1 回答 1

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
于 2013-05-01T23:18:35.683 回答