Обновить текстовое поле в другую форму от фонового работника - PullRequest
0 голосов
/ 27 июня 2018

У меня есть две формы, Form1 и Newform. Form1 имеет две кнопки и текстовое поле, а Newform имеет собственное текстовое поле. Я использую подпрограмму settext для вызова подпрограммы делегата в фоновом режиме, чтобы обновить текстовое поле в обеих формах.

Кажется, что текстовое поле в Form1 обновляется, но текстовое поле в Newform не обновляется.

Есть ли что-то, чего мне не хватает, если я хочу обновить текстовое поле в другой форме?

Спасибо заранее.

Imports System.Threading
Public Class Form1
Dim stopbit As Boolean
Dim TestingComplete As Boolean
Dim ReadValue As Double
Dim FinalValue As Double

Delegate Sub SetTextCallback(ByRef Txtbox As TextBox, ByVal Txt As String)

'Thread Safe textbox update routine
Private Sub SetText(ByRef Txtbox As TextBox, ByVal Txt As String)

    ' InvokeRequired required compares the thread ID of the
    ' calling thread to the thread ID of the creating thread.
    ' If these threads are different, it returns true.
    Console.WriteLine(Txtbox.InvokeRequired & "  textbox invokerequired")

    If Txtbox.InvokeRequired Then
        Try
            'MsgBox("inside settext")
            Txtbox.Invoke(New SetTextCallback(AddressOf SetText), Txtbox, Txt)
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    Else
        Txtbox.Text = Txt
        Txtbox.Update()
    End If
End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    newform.Show()
End Sub

Function ReadTemp() As Double
    ReadValue = ReadValue / 2
    Return ReadValue
End Function

Sub Test()
    Dim starttime As Integer
    Dim EllapsedTime As Integer
    Dim OldValue As Double = 0
    Dim NewValue As Double = 0
    Dim Difference As Double = 1
    Dim Margin As Double = 0.1

    stopbit = False
    starttime = My.Computer.Clock.TickCount
    Do
        Thread.Sleep(200)
        OldValue = NewValue
        NewValue = ReadTemp()
        Difference = Math.Abs(NewValue - OldValue)
        SetText(Me.TextBox1, Difference.ToString)
        SetText(newform.TextBox1, Difference.ToString)
        newform.Refresh()
        EllapsedTime = My.Computer.Clock.TickCount - starttime


    Loop Until EllapsedTime > 5000 Or stopbit = True ' Or Difference < Margin
    FinalValue = NewValue
    TestingComplete = True

End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    BackgroundWorker1.RunWorkerAsync()
End Sub

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

Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

    For i As Integer = 1 To 10
        ReadValue = 100000
        TestingComplete = False
        ThreadPool.QueueUserWorkItem(AddressOf Test)

        Do
            Thread.Sleep(200)
        Loop Until TestingComplete = True
        MsgBox("Final Value  " & FinalValue)
    Next

End Sub

Конечный класс

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

Ваша проблема связана с тем, что вы используете экземпляр по умолчанию из newform. В VB.NET экземпляры форм по умолчанию - это функция, которая позволяет вам получить доступ к форме через имя типа, не создавая ее вручную.

Другими словами, это позволяет вам сделать это:

newform.Show()
newform.TextBox1.Text = "Something"

... вместо того, чтобы делать это правильным образом, а именно:

Dim myNewForm As New newform
myNewForm.Show()
myNewForm.TextBox1.Text = "Something"

Выше мы создаем новый экземпляр newform с именем myNewForm. Это необходимо для возможности использования большинства объектов в каркасе (включая формы). Однако VB.NET упрощает это поведение, предлагая создать экземпляр для вас, что и происходит в моем первом примере.

Проблема с этими экземплярами по умолчанию заключается в том, что они специфичны для каждого потока, то есть для каждого потока создается новый экземпляр *1021*, в котором вы используете это поведение.

Таким образом, форма, на которую вы ссылаетесь, когда вы делаете:

newform.Show()

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

'This is not the same "newform" as above!
SetText(newform.TextBox1, Difference.ToString)

Решением этой проблемы, конечно же, является создание экземпляра самостоятельно, что позволяет вам полностью контролировать происходящее:

Dim newFrm As New newform

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    newFrm.Show()
End Sub

...your code...

Sub Test()
    ...your code...

    SetText(newFrm.TextBox1, Difference.ToString)

    ...even more of your code...
End Sub

В качестве дополнительной заметки вы можете удалить свои звонки на newform.Refresh() и Txtbox.Update(). Это просто приводит к ненужным накладным расходам, заставляя форму и текстовые поля перерисовывать себя, что уже сделано, когда вы изменяете любое из их свойств, которые влияют на их содержимое / дизайн (таким образом, вы фактически заставляете их перерисовывать себя дважды).

Кроме того, если вы хотите сделать вызов в потоке пользовательского интерфейса проще и используете Visual Studio / Visual Basic 2010 или новее , вы можете переключиться к использованию лямбда-выражений вместо обычных делегатов. Они намного проще в использовании и позволяют создавать целые встроенные методы, которые можно вызывать в потоке пользовательского интерфейса.

Для этой цели я написал метод расширения под названием InvokeIfRequired(), который позволяет вам вызывать любой метод / функцию в потоке пользовательского интерфейса, проверяя InvokeRequired для вас. Это похоже на то, что у вас есть сейчас, только оно работает для любого элемента управления (не только для текстовых полей) и с лямбда-выражениями, позволяет запускать любой код, который вы хотите в пользовательском интерфейсе.

Вы можете использовать его, добавив модуль в ваш проект (Add New Item... > Module) и присвоив ему имя Extensions. Затем поместите этот код внутри него:

Imports System.Runtime.CompilerServices

Public Module Extensions
    ''' <summary>
    ''' Invokes the specified method on the calling control's thread (if necessary, otherwise on the current thread).
    ''' </summary>
    ''' <param name="Control">The control which's thread to invoke the method at.</param>
    ''' <param name="Method">The method to invoke.</param>
    ''' <param name="Parameters">The parameters to pass to the method (optional).</param>
    ''' <remarks></remarks>
    <Extension()> _
    Public Function InvokeIfRequired(ByVal Control As Control, ByVal Method As [Delegate], ByVal ParamArray Parameters As Object()) As Object
        If Parameters IsNot Nothing AndAlso _
            Parameters.Length = 0 Then Parameters = Nothing

        If Control.InvokeRequired = True Then
            Return Control.Invoke(Method, Parameters)
        Else
            Return Method.DynamicInvoke(Parameters)
        End If
    End Function
End Module

Это позволяет вам вызвать одну строку кода, выполнив:

Me.InvokeIfRequired(Sub() Me.TextBox1.Text = Difference.ToString())

Или вызвать целый блок кода, выполнив:

Me.InvokeIfRequired(Sub()
                        Me.TextBox1.Text = Difference.ToString()
                        newFrm.TextBox1.Text = Difference.ToString()
                        Me.BackColor = Color.Red 'Just an example of what you can do.
                    End Sub)
0 голосов
/ 27 июня 2018
YourSubHere

Me.Invoke(Sub()
          Form1.Textbox1.text="some text1"
          Form2.Textbox2.text="some text2"
          End Sub)
End Sub

Или, если это один лайнер.

Me.Invoke(Sub() Form1.Textbox1.text="some text1")

В зависимости от того, что вам нужно, вы можете использовать только некоторые элементы управления, такие как:

Textbox1.invoke(Sub() Textbox1.text="some text1")
...