2017-02-18 38 views
0

Я не уверен, почему этот код работает, когда вызывается из потока пользовательского интерфейса, а не иначе:Изменение свойства элемента управления с помощью делегата

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) и изменить его значение. Любая помощь была бы оценена

+0

Если вам не нужно ссылаться, вы напрямую изменяете ссылку 'ControlProperty'. Однако, когда вы вызываете 'Invoke()' параметры передаются как массив объектов, где каждый элемент массива передается по значению. Это стандартное поведение массивов и невозможно изменить. - Используйте решение @ MrGadget, которое вызывает метод напрямую и по-прежнему передает 'ControlProperty' по ссылке. –

+0

К сожалению, кажется, что параметр ByRef не может использоваться в выражении лямбда @VisualVincent – Kyle

+0

Но выражение лямбда должно вести себя как любой нормальный метод, а вызов другого метода с параметром ByRef не должен иметь значения. Я попробую, и если я не получу его на работу, у меня есть альтернативное решение, которое вы можете использовать. –

ответ

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 вызовет исключение.

+0

Спасибо. Это будет работать для меня – Kyle

+0

@Kyle: Рад, что я мог бы помочь! Удачи с вашим проектом! –

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

+0

Это должно сработать. Он передает параметр по ссылке на себя так, как должен.Однако нет необходимости вызывать 'Refresh()' на любом элементе управления, поскольку они будут автоматически перерисовываться при изменении их текста. –

+0

В нем говорится, что параметр ByRef не может использоваться в выражении лямбда - вот почему я попытался использовать делегат – Kyle

+0

Заменен мой ответ тестовым случаем ... будет снова обновляться по мере разработки ответа. – MrGadget

 Смежные вопросы

  • Нет связанных вопросов^_^