触发自定义类事件后,表单未更新

发布于 2024-09-25 00:12:08 字数 5988 浏览 3 评论 0原文

我遇到一个问题,即使我看到事件触发,我的主表单也没有更新。让我解释一下情况并分享一些我的代码,因为我是业余爱好者,所以我确信这些代码会很糟糕。

我创建了一个类来获取在后台运行进程的设置。我在该类中添加了一些自定义事件,以便我可以在表单中使用它而不是计时器。

我在两个子进程上设置了一个中断来处理这些事件,并且我看到它们在安装开始后立即启动。

我查看了数据,发现没有抛出任何异常。

起初我认为这是因为 datagridview 有一些延迟问题。我通过我发现的一些技巧将其设置为双缓冲,但这并不重要。数据显示在数据网格中之前仍有大约 10 秒的延迟。

我想了想,决定我真的不需要 datagridview 并用多行文本框替换该控件,但这并没有什么区别。显示表单/文本框的更新仍然需要 10 秒或更长时间。

我在下面包含了一些代码。

Public Shared WithEvents np As NewProcess

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Try
            np = New NewProcess
            AddHandler np.InstallFinished, AddressOf np_InstallFinished
            AddHandler np.InstallStarted, AddressOf np_InstallStarted
        Catch ex As Exception

        End Try

    End Sub

Protected Sub np_InstallFinished(ByVal Description As String, ByVal ExitCode As Integer)
    InstallInProcess = False

    If Not Description = Nothing Then
        If Not ExitCode = Nothing Then
            AddLog(String.Format("Completed install of {0} ({1}).", Description, ExitCode))
        Else
            AddLog(String.Format("Completed install of {0}.", Description))
        End If
    End If
    RefreshButtons()
    UpdateListofApps()
    np.Dispose()
End Sub

Protected Sub np_InstallStarted(ByVal Description As String)
    InstallInProcess = True

    If Not Description = Nothing Then AddLog(String.Format("Started the install of {0}.", Description))
End Sub

Public Class NewProcess
    Dim ProcessName As String
    Dim ProcessVisibile As Boolean
    Dim Arguments As String
    Dim WaitforExit As Boolean
    Dim Description As String
    Dim ShellExecute As Boolean
    Dim EC As Integer = Nothing 'Exit Code
    Private IsBusy As Boolean = Nothing
    Dim th As Threading.Thread

    Public Event InstallFinished(ByVal Description As String, ByVal ExitCode As Integer)

    Public Event InstallStarted(ByVal Description As String)

    Public Function Busy() As Boolean
        If IsBusy = Nothing Then Return False
        Return IsBusy
    End Function

    Public Function ExitCode() As Integer
        Return EC
    End Function

    Public Function ProcessDescription() As String
        Return Description
    End Function

    ''' <summary>
    ''' Starts a new multithreaded process.
    ''' </summary>
    ''' <param name="path">Path of the File to run</param>
    ''' <param name="Visible">Should application be visible?</param>
    ''' <param name="Arg">Arguments</param>
    ''' <param name="WaitforExit">Wait for application to exit?</param>
    ''' <param name="Description">Description that will show up in logs</param>
    ''' <remarks>Starts a new multithreaded process.</remarks>
    Public Sub StartProcess(ByVal path As String, ByVal Visible As Boolean, Optional ByVal Arg As String = Nothing, Optional ByVal WaitforExit As Boolean = False, Optional ByVal Description As String = Nothing)

        Try
            Me.ProcessName = path
            Me.ProcessVisibile = Visible
            If Arguments = Nothing Then Me.Arguments = Arg
            Me.Description = Description
            Me.WaitforExit = WaitforExit

            If IsBusy And WaitforExit Then
                MessageBox.Show("Another install is already in process, please wait for previous install to finish.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                Exit Sub
            End If

            If Not fn_FileExists(ProcessName) Then
                MessageBox.Show("Could not find file " & ProcessName & ".", "Could not start process because file is missing.", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Exit Sub
            End If

            th = New Threading.Thread(AddressOf NewThread)

            With th
                .IsBackground = True
                If Not Description Is Nothing Then .Name = Description
                .Start()
            End With
        Catch ex As Exception

        End Try
    End Sub

    Private Sub NewThread()
        Dim p As Process

        Try
            p = New Process

            With p
                .EnableRaisingEvents = True
                .StartInfo.Arguments = Arguments
                .StartInfo.FileName = ProcessName
                .StartInfo.CreateNoWindow = ProcessVisibile
            End With

            If ProcessVisibile Then
                p.StartInfo.WindowStyle = ProcessWindowStyle.Normal
            Else
                p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
            End If

            p.Start()
            IsBusy = True
            RaiseEvent InstallStarted(Description)

            If WaitforExit Then
                Do While p.HasExited = False
                    Threading.Thread.Sleep(500)
                Loop
                IsBusy = False
                RaiseEvent InstallFinished(Description, p.ExitCode)
            End If

            EC = p.ExitCode  

        Catch ex As Exception

        End Try
    End Sub

    Public Sub Dispose()
        ProcessName = Nothing
        ProcessVisibile = Nothing
        Arguments = Nothing
        WaitforExit = Nothing
        Description = Nothing
        EC = Nothing
        InstallInProcess = Nothing
        th.Join()
        MemoryManagement.FlushMemory()
    End Sub

End Class

Sub AddLog(ByVal s As String)
    Try

        s = String.Format("[{0}] {1}", TimeOfDay.ToShortTimeString, s)

        Form1.tbLogs.AppendText(s & vbCrLf)

        Using st As New StreamWriter(LogFilePath, True)
            st.WriteLine(s)
            st.Flush()
        End Using

    Catch ex As Exception
    End Try
End Sub

有什么想法吗?我完全不知所措。

我尝试添加 application.doevents、me.refresh 和其他一些东西:(

I'm having an issue where my main form isn't updating even though I see the event fire off. Let me explain the situation and share some of my code which I'm sure will be horrible since I'm an amateur.

I created a class to take in the settings for running a process in the background. I add some custom events in that class so I could use that in my form instead of a timer.

I put a break on the two subs for that handle those events and I see them get kicked off as soon as an install starts.

I look at the data and it's coming across and no exceptions are thrown.

At first I thought it was because the datagridview had some latency issues. I set that to be double buffered through some tricks I found but it didn't matter. There was still a roughly 10 second delay before the data showed up in the datagrid.

I thought about it and decided I really didn't need a datagridview and replaced the control with a multiline textbox, but it didn't make a difference. It's still taking 10 seconds or longer to show updates to the form/textbox.

I've included some of my code below.

Public Shared WithEvents np As NewProcess

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Try
            np = New NewProcess
            AddHandler np.InstallFinished, AddressOf np_InstallFinished
            AddHandler np.InstallStarted, AddressOf np_InstallStarted
        Catch ex As Exception

        End Try

    End Sub

Protected Sub np_InstallFinished(ByVal Description As String, ByVal ExitCode As Integer)
    InstallInProcess = False

    If Not Description = Nothing Then
        If Not ExitCode = Nothing Then
            AddLog(String.Format("Completed install of {0} ({1}).", Description, ExitCode))
        Else
            AddLog(String.Format("Completed install of {0}.", Description))
        End If
    End If
    RefreshButtons()
    UpdateListofApps()
    np.Dispose()
End Sub

Protected Sub np_InstallStarted(ByVal Description As String)
    InstallInProcess = True

    If Not Description = Nothing Then AddLog(String.Format("Started the install of {0}.", Description))
End Sub

Public Class NewProcess
    Dim ProcessName As String
    Dim ProcessVisibile As Boolean
    Dim Arguments As String
    Dim WaitforExit As Boolean
    Dim Description As String
    Dim ShellExecute As Boolean
    Dim EC As Integer = Nothing 'Exit Code
    Private IsBusy As Boolean = Nothing
    Dim th As Threading.Thread

    Public Event InstallFinished(ByVal Description As String, ByVal ExitCode As Integer)

    Public Event InstallStarted(ByVal Description As String)

    Public Function Busy() As Boolean
        If IsBusy = Nothing Then Return False
        Return IsBusy
    End Function

    Public Function ExitCode() As Integer
        Return EC
    End Function

    Public Function ProcessDescription() As String
        Return Description
    End Function

    ''' <summary>
    ''' Starts a new multithreaded process.
    ''' </summary>
    ''' <param name="path">Path of the File to run</param>
    ''' <param name="Visible">Should application be visible?</param>
    ''' <param name="Arg">Arguments</param>
    ''' <param name="WaitforExit">Wait for application to exit?</param>
    ''' <param name="Description">Description that will show up in logs</param>
    ''' <remarks>Starts a new multithreaded process.</remarks>
    Public Sub StartProcess(ByVal path As String, ByVal Visible As Boolean, Optional ByVal Arg As String = Nothing, Optional ByVal WaitforExit As Boolean = False, Optional ByVal Description As String = Nothing)

        Try
            Me.ProcessName = path
            Me.ProcessVisibile = Visible
            If Arguments = Nothing Then Me.Arguments = Arg
            Me.Description = Description
            Me.WaitforExit = WaitforExit

            If IsBusy And WaitforExit Then
                MessageBox.Show("Another install is already in process, please wait for previous install to finish.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                Exit Sub
            End If

            If Not fn_FileExists(ProcessName) Then
                MessageBox.Show("Could not find file " & ProcessName & ".", "Could not start process because file is missing.", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Exit Sub
            End If

            th = New Threading.Thread(AddressOf NewThread)

            With th
                .IsBackground = True
                If Not Description Is Nothing Then .Name = Description
                .Start()
            End With
        Catch ex As Exception

        End Try
    End Sub

    Private Sub NewThread()
        Dim p As Process

        Try
            p = New Process

            With p
                .EnableRaisingEvents = True
                .StartInfo.Arguments = Arguments
                .StartInfo.FileName = ProcessName
                .StartInfo.CreateNoWindow = ProcessVisibile
            End With

            If ProcessVisibile Then
                p.StartInfo.WindowStyle = ProcessWindowStyle.Normal
            Else
                p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
            End If

            p.Start()
            IsBusy = True
            RaiseEvent InstallStarted(Description)

            If WaitforExit Then
                Do While p.HasExited = False
                    Threading.Thread.Sleep(500)
                Loop
                IsBusy = False
                RaiseEvent InstallFinished(Description, p.ExitCode)
            End If

            EC = p.ExitCode  

        Catch ex As Exception

        End Try
    End Sub

    Public Sub Dispose()
        ProcessName = Nothing
        ProcessVisibile = Nothing
        Arguments = Nothing
        WaitforExit = Nothing
        Description = Nothing
        EC = Nothing
        InstallInProcess = Nothing
        th.Join()
        MemoryManagement.FlushMemory()
    End Sub

End Class

Sub AddLog(ByVal s As String)
    Try

        s = String.Format("[{0}] {1}", TimeOfDay.ToShortTimeString, s)

        Form1.tbLogs.AppendText(s & vbCrLf)

        Using st As New StreamWriter(LogFilePath, True)
            st.WriteLine(s)
            st.Flush()
        End Using

    Catch ex As Exception
    End Try
End Sub

Any idea's? I'm at a complete loss.

I've tried adding application.doevents, me.refresh and quite a few other things :(

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

花辞树 2024-10-02 00:12:08
    Form1.tbLogs.AppendText(s & vbCrLf)

标准 VB.NET 陷阱。 Form1 是类名,而不是对表单的引用。不幸的是,VB.NET 在 VB6 中实现了不合时宜的做法,而 VB6 是合法的。然而,当您使用线程时,它就会崩溃。您将自动创建另一个表单对象,该对象不可见,因为从未调用其 Show() 方法。否则就死定了,因为线程没有泵送消息循环。

您需要将对用户正在查看的实际表单对象的引用传递给工作器类。 Form1 代码中 Me 的值。您还必须使用 Control.Invoke,因为从另一个线程更新控件是不合法的。我建议您改为触发一个事件,即 Form1 可以订阅的事件,这样您的工作线程类就不会受到 UI 实现细节的影响。

    Form1.tbLogs.AppendText(s & vbCrLf)

Standard VB.NET trap. Form1 is a class name, not a reference to the form. Unfortunately, VB.NET implemented an anachronism from VB6 where that was legal. It however falls apart when you use threads. You'll get another form object automatically created, one that isn't visible because its Show() method was never called. Otherwise dead as a doornail since the thread is not pumping a message loop.

You'll need to pass a reference to the actual form object that the user is looking at to the worker class. The value of Me in the Form1 code. You will also have to use Control.Invoke since it isn't legal to update controls from another thread. I recommend you fire an event instead, one that Form1 can subscribe to, so that your worker class isn't infected with implementation details of the UI.

叹沉浮 2024-10-02 00:12:08

一些建议:

  1. 首先使其在没有线程的情况下工作。 (讽刺的是,你称自己为业余爱好者,碰巧我只是在过了“业余”阶段后才学会这样做)
  2. 不要尝试从后台线程更新 GUI 控件。这在 Windows 中是被禁止的。我不确定你是这么做的(不是 VB 大师),但看起来确实是这样。
  3. 使用 .net BackgroundWorker 类。它具有从后台线程与主线程对话的内置功能。这并不完美,但却是一个好的开始。

Some suggestions:

  1. First make it work without threads. (Ironical you call yourself an amateur, It so happened I only learned to do that after I was past the 'amateur' stage)
  2. Don't try to update the GUI Controls from a background thread. That's forbidden in windows. I'm not sure that's what you do (no VB guru), but it sure looks like it.
  3. Use the .net BackgroundWorker class. It has builtin functionality to talk back to the main thread from a background thread. It's not perfect but a good start.
夜唯美灬不弃 2024-10-02 00:12:08

你为我指明了正确的方向。谢谢汉斯。这是我的解决方案:

Private Sub SetText(ByVal [text] As String)

    If Me.tbLogs.InvokeRequired Then
        Dim d As New SetTextCallback(AddressOf SetText)
        Me.Invoke(d, New Object() {[text]})
    Else
        Me.tbLogs.Text = [text]
    End If
End Sub

Private Sub np_InstallStarted(ByVal Description As String)
    InstallInProcess = True
    If Me.tbLogs.Text = "" Then
        SetText(String.Format("[{0}] Started the install of {1}.{2}", TimeOfDay.ToShortTimeString, Description, vbCrLf))
    Else
        SetText(tbLogs.Text & vbCrLf & String.Format("[{0}] Started the install of {1}.{2}", TimeOfDay.ToShortTimeString, Description, vbCrLf))
    End If

End Sub

Private Sub np_InstallFinished(ByVal [Description] As String, ByVal [ExitCode] As Integer)
    InstallInProcess = False

    If Not Description = Nothing Then
        If Not ExitCode = Nothing Then
            SetText(tbLogs.Text & vbCrLf & String.Format("[{0}] Completed install of {1} ({2}).{3}", TimeOfDay.ToShortTimeString, Description, ExitCode, vbCrLf))
        Else
            SetText(tbLogs.Text & vbCrLf & String.Format("[{0}] Completed install of {1}.{3}", TimeOfDay.ToShortTimeString, Description, vbCrLf))
        End If
    End If
    RefreshButtons()
    UpdateListofApps()
    np.Dispose()
End Sub

因此,当安装已开始或完成的事件开始时,我使用 SetText 更新原始表单上的日志。

问题是我以“未注册用户”的身份发布了原始帖子,所以现在我试图找出一种方法来表示问题已得到解答。再次感谢您的帮助!

You got me pointed in the right direction. Thanks Hans. This was my solution:

Private Sub SetText(ByVal [text] As String)

    If Me.tbLogs.InvokeRequired Then
        Dim d As New SetTextCallback(AddressOf SetText)
        Me.Invoke(d, New Object() {[text]})
    Else
        Me.tbLogs.Text = [text]
    End If
End Sub

Private Sub np_InstallStarted(ByVal Description As String)
    InstallInProcess = True
    If Me.tbLogs.Text = "" Then
        SetText(String.Format("[{0}] Started the install of {1}.{2}", TimeOfDay.ToShortTimeString, Description, vbCrLf))
    Else
        SetText(tbLogs.Text & vbCrLf & String.Format("[{0}] Started the install of {1}.{2}", TimeOfDay.ToShortTimeString, Description, vbCrLf))
    End If

End Sub

Private Sub np_InstallFinished(ByVal [Description] As String, ByVal [ExitCode] As Integer)
    InstallInProcess = False

    If Not Description = Nothing Then
        If Not ExitCode = Nothing Then
            SetText(tbLogs.Text & vbCrLf & String.Format("[{0}] Completed install of {1} ({2}).{3}", TimeOfDay.ToShortTimeString, Description, ExitCode, vbCrLf))
        Else
            SetText(tbLogs.Text & vbCrLf & String.Format("[{0}] Completed install of {1}.{3}", TimeOfDay.ToShortTimeString, Description, vbCrLf))
        End If
    End If
    RefreshButtons()
    UpdateListofApps()
    np.Dispose()
End Sub

So when the event kicks off that the install has started or finished, I use the SetText to update the log on the original form.

Problem is I posted that original post as an "unregistered user" so now I'm trying to figure out a way to say the question was answered. Thanks again for your help!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文