通过委托更改控件的属性

时间:2017-02-18 04:05:37

标签: .net vb.net multithreading user-interface delegates

我不确定从UI线程调用此代码的原因,但不是这样:

Delegate Sub CtrlPropertyChangeDelegate(ByRef ControlProperty As Object, ByVal NewValue As Object)

Sub CtrlPropertyChange(ByRef ControlProperty As Object, ByVal NewValue As Object)

    If Me.InvokeRequired Then
        Me.Invoke(New CtrlPropertyChangeDelegate(AddressOf CtrlPropertyChange), ControlProperty, NewValue)
    Else
        ControlProperty = NewValue
    End If

End Sub

它应该使用控件的属性(例如Form1.Text)并更改其值。任何帮助将不胜感激

2 个答案:

答案 0 :(得分:1)

更新以合并@VisualVincent's Extension,应将其标记为正确答案。

Public Class Form1
    Private Rand As New Random
    Private Done As Boolean = True
    Private WordList() As String = {"One", "Two", "Three", "Four"}
    Private Colors() As Color = {Color.Red, Color.Blue, Color.Gray, Color.Yellow}

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Done Then
            Done = False
            Task.Run(Sub() DoSomething())
        Else
            Done = True
        End If
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        RandomChange()
    End Sub

    Sub DoSomething()
        Me.Button1.SetProperty("Text", "Stop")

        Do Until Done
            RandomChange()
            Threading.Thread.Sleep(1000)
        Loop

        Me.Button1.SetProperty("Text", "Start")
    End Sub

    Sub RandomChange()
        Select Case Rand.Next(1, 6)
            Case 1
                Me.ProgressBar1.SetProperty("Value", Rand.Next(10, 90))
            Case 2
                Me.TextBox1.SetProperty("Text", WordList(Rand.Next(0, 4)))
            Case 3
                Me.Label1.SetProperty("Text", WordList(Rand.Next(0, 4)))
            Case 4
                Me.CheckBox1.SetProperty("Checked", Not Me.CheckBox1.Checked)
            Case 5
                Me.SetProperty("BackColor", Colors(Rand.Next(0, 4)))
        End Select
    End Sub
End Class

Screenshot

答案 1 :(得分:1)

正如我的评论中已经提到的,一旦您将ControlProperty传递给Control.Invoke()的参数,就不再通过引用传递它。

这是使用Reflection的解决方案。您只需将属性的名称和要传递给SetProperty方法的值传递给它。我已将其声明为扩展方法,因此必须将其放入公共模块中:

Imports System.Reflection
Imports System.Runtime.CompilerServices

Public Module Extensions
    ''' <summary>
    ''' Thread-safely sets the property of a control to the specified value. Be sure that the value is of the correct type.
    ''' </summary>
    ''' <param name="Control">The control which's property to set.</param>
    ''' <param name="PropertyName">The name of the property to set.</param>
    ''' <param name="Value">The value to give the property.</param>
    <Extension()> _
    Public Sub SetProperty(ByVal Control As Control, ByVal PropertyName As String, ByVal Value As Object)
        If Control.InvokeRequired = True Then
            Control.Invoke(Sub() Control.SetProperty(PropertyName, Value))
        Else
            Control.GetType().InvokeMember(PropertyName, _
                                            BindingFlags.SetProperty _
                                             Or BindingFlags.IgnoreCase _
                                              Or BindingFlags.Public _
                                               Or BindingFlags.Instance _
                                                Or BindingFlags.Static, _
                                            Nothing, Control, New Object() {Value})
        End If
    End Sub
End Module

现在您可以像这样使用它:

For x = 1 To 256
    Label1.SetProperty("Text", ((x * 100) / 256) & "%")
    ProgressBar1.SetProperty("Value", x)
    Thread.Sleep(15)
Next

重要提示:使用此功能时,必须确保您提供属性的值的类型正确,因为不会执行隐式转换。因此,如果您设置的属性属于String类型,则尝试将其设置为Integer将引发异常。