为什么UserControl中的System.Windows.Forms.Timer不会触发其Tick事件?

时间:2017-09-21 06:25:59

标签: vb.net winforms

我有一个UserControl,我在其上添加了一个System.Windows.Forms.Timer。计时器是在设计时添加的。默认情况下,Enabled属性为False。我刚把它的间隔改为400毫秒。

UserControl以编程方式添加到表单中。计时器在设计时被禁用。在运行时,计时器由UserControl的Property Setter启用。

UserControl类是这样的:

Public Class LuminaireSign
   Public Property MyLuminaire As Luminaire
Private _state As LuminaireSignState
Public Property State As LuminaireSignState
    Get
        Return _state
    End Get
    Set(value As LuminaireSignState)
        If value <> _state Then
            _state = value

            If _state = LuminaireSignState.OK Then
                Timer1.Enabled = False
                Me.Image = luminSign_Green
            Else
                Timer1.Enabled = True
            End If

        End If
    End Set
End Property

Public Sub New(ByRef associatedLumin As Luminaire)

    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.

    MyLuminaire = associatedLumin
    AddHandler MyLuminaire.PropertyChanged, New PropertyChangedEventHandler(AddressOf propChanged)
End Sub

Private Sub propChanged(sender As Object, e As PropertyChangedEventArgs)
    If e.PropertyName = "Status" Then
        If MyLuminaire.Status.BatteryEmpty Then
            State = LuminaireSignState.Fault
        Else
            State = LuminaireSignState.OK
        End If
    End If
End Sub

奇怪的是,即使计时器获得启用,计时器的Tick事件也不会被触发。

如您所见,我通过Sub propChanged()设置State属性,它是一个处理MyLuminaire.PropertyChanged的PropertyChangedEventHandler。 MyLuminaire在UserControl的构造函数中通过引用传递。实际灯具对象的状态在非UI线程中设置。

难道不能勾选,因为propChanged事件是通过非UI线程属性更改触发的吗? 有人可以解释一下发生了什么吗?

PS。如果我在设计时启用计时器,则Tick事件会一直触发,直到计时器被禁用一次。然后,即使计时器变为启用,它也不会触发Tick事件。

1 个答案:

答案 0 :(得分:0)

问题是propertyChanged发生在后台线程中,因此计时器无法触发Tick事件。所以我们必须以线程安全的方式启用计时器!

解决方案是不直接设置timer1.Enabled,而是创建一个子(请参阅SetStateOk),设置timer1和每当InvokeRequired = true时调用此子的委托。

因此,更改State属性并添加一个调用SetStateOk Sub并解决问题:

Public Property State As LuminaireSignState
Get
    Return _state
End Get
Set(value As LuminaireSignState)
    If value <> _state Then
        _state = value

        If _state = LuminaireSignState.OK Then
             SetStateOk(true)
            'Timer1.Enabled = False
            'Me.Image = luminSign_Green
        Else
            SetStateOk(false)
            'Timer1.Enabled = True
        End If

    End If
End Set
End Property


Private Delegate Sub SetStateOKDelegate(ByVal stateIsOk As Boolean)

Private Sub SetStateOK(ByVal stateIsOk As Boolean)
    If Me.InvokeRequired Then
        Me.Invoke(New SetStateOKDelegate(AddressOf SetStateOK), New Object() {stateIsOk})
    Else
        If stateIsOk Then
            timer1.Enabled = False
            Image = luminSign_Green
        Else
            timer1.Enabled = True
        End If
    End If
End Sub

通过这种方式启用计时器并定期触发Tick事件!