如何从VB NET中的线程更改窗口的状态?



我认为我有一个相当简单的问题,但即使在论坛上花费了相当长的时间并尝试了不同的解决方案之后,我也无法解决它。 问题如下,我有一个通过表单(Form1(与用户交互的软件。当用户与软件交互时,我需要每秒记录一次温度,并在温度超过阈值时发出警报。 为了监视温度,我启动一个新线程,该线程使用永久循环在 Form1 加载时检查温度,并在 Form1 关闭时中止它。 为了发出警报,我创建了一个新表单(AlertWindow(,其中仅包含一条简单的消息(Label="太热"(。但是,我发现无法从线程控制窗口的行为(即,如果满足太热的条件,则线程中的 AlertWindow.Show((。

到目前为止我尝试过:

线程
  • 中的MsgBox:很好,因为它出现了,但它停止了线程,从而停止了温度的记录。我只想发出警报并继续监测温度。

  • 线程中的 AlertWindow.Show((:不起作用,窗口打开,但你看不到消息,你无法与窗互(即关闭它(。此外,如果您在线程开始之前已经显示了窗口(表单加载时的 AlertWindow.Show((,线程会打开一个新窗口(因此线程不会按预期控制窗口,而是创建该窗口的新实例......

  • 事件处理:我认为这将是理想的...我创建了一个处理警报事件的子。子只是显示警报窗口。当温度太热时,我在线程中提出事件。问题与以前相同,警报窗口打开,但它再次被阻止,并且您看不到其中写入的消息。因此,线程似乎正在创建窗口的新实例,而不是简单地更改其行为。

  • 委托
  • :我没有尝试实现委托,因为我不想从线程返回值,而是在事件发生时更改警报窗口的行为。

这是我能想到的最简单的代码,可以重现我的问题(调用从线程显示警报窗口的 sub(:

Imports System.Threading
Public Class Form1
Dim ThreadLoop As New Thread(AddressOf PermanentLoop)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ThreadLoop.Start()
End Sub
Private Sub PermanentLoop()
Do 'Here I measure the temperature
ShowAlert()
Thread.Sleep(1 * 1000)
Loop While True
End Sub
Public Sub ShowAlert()
AlertWindow.Show()
End Sub
Private Sub Form1_Closing(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
ThreadLoop.Abort()
End Sub
End Class

任何帮助将不胜感激。谢谢!

尝试使用线程安全调用来显示和更新 UI 线程中的窗体:

Public Sub ShowAlert()
If Me.InvokeRequired = True Then
Me.Invoke(AddressOf ShowAlert)
Else
AlertWindow.Show()
End If
End Sub
Public Sub UpdateAlertText(ByVal Text As String)
If Me.InvokeRequired = True Then
Me.Invoke(Sub() UpdateAlertText(Text))
Else
AlertWindow.Label1.Text = Text
End If
End Sub

现在在您的线程中,您可以执行以下操作:

Do
ShowAlert()
UpdateAlertText("Blah blah")
Thread.Sleep(1 * 1000)
Loop While True

无法从非 UI 线程打开 UI 元素。您必须在 UI 线程上执行与 UI 相关的所有操作。

但请不要胡思乱想。而是使用旨在正确处理此类事情的库。我建议使用Microsoft的响应式框架(Rx(。

您将编写以下代码:

Private _subscription As IDisposable
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
_subscription = Observable.Interval(TimeSpan.FromSeconds(1.0)).ObserveOn(Me).Subscribe(Sub(x) ShowAlert())
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
_subscription.Dispose()
End Sub

就是这样。简单。

您需要NuGet "System.Reactive.Windows.Forms"。

如果你想变得更聪明,你可以这样做:

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
_subscription = _
Observable _
.Interval(TimeSpan.FromSeconds(1.0)) _
.Where(Function(x) GetTemperature() > threshold) _
.ObserveOn(Me) _
.Subscribe(Sub(x) ShowAlert())
End Sub

最新更新