Another option is to model the concept of an animatable value with its own type:
vbnet Code:
Public Interface IAnimatable(Of T)
Property AnimationValue As T
End Interface
Which you can provide a concrete implementation for animating the width of a control - which can as part of its implementation deal with the issues of cross threading:
vbnet Code:
Public Class ControlWidthAnimator
Implements IAnimatable(Of Integer)
Private _control As Control
Private _width As Integer
Public Sub New(control As Control)
_control = control
_width = _control.Width
End Sub
Public Property AnimationValue As Integer Implements IAnimatable(Of Integer).AnimationValue
Get
Return _width
End Get
Set(value As Integer)
_width = value
SetWidth(_width)
End Set
End Property
Private Sub SetWidth(newWidth As Integer)
If (_control.InvokeRequired) Then
_control.Invoke(New Action(Of Integer)(AddressOf SetWidth), newWidth)
Else
_control.Width = newWidth
End If
End Sub
End Class
Allowing your Tweener code to deal with the abstract concept of an animatable value:
vbnet Code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t As New Threading.Thread(Sub() Tweener(New ControlWidthAnimator(Button1), 100, 400))
t.Start()
End Sub
Public Sub Tweener(ByVal animatable As IAnimatable(Of Integer), ByVal Amount As Integer, ByVal tPeriod As Integer)
Dim stopat As Integer = Environment.TickCount + tPeriod
Dim tFrame = 40
Dim chunk As Integer = Amount / (tPeriod / tFrame)
While Environment.TickCount < stopat
animatable.AnimationValue += chunk
Application.DoEvents()
Threading.Thread.Sleep(tFrame)
End While
End Sub
(I'd still deal with the animation with some kind of timer (be that a Windows.Forms.Timer on the UI thread or a System.Threading.Timer that returns on a worker thread) rather than sleeping in a worker thread, but now that's an orthogonal aspect of the code that can be altered independently of how the control itself gets altered.)