2010-09-24 19 views
2

我遇到問題,即使看到事件發生,我的主窗體仍未更新。讓我解釋一下我的情況,並分享我的一些代碼,由於我是業餘愛好者,所以我敢肯定這些代碼會很糟糕。在自定義類事件被觸發後,表單不會更新

我創建了一個類來接受在後臺運行進程的設置。我在該類中添加了一些自定義事件,所以我可以在表單中使用它而不是定時器。

爲了處理這些事件,我放了兩個子節點,我發現它們在安裝開始後立即啓動。

我看了一下數據,發現並沒有例外。

起初我以爲這是因爲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和不少其他東西:(

回答

5
Form1.tbLogs.AppendText(s & vbCrLf) 

標準VB.NET陷阱。 Form1是一個類名,而不是對錶單的引用。不幸的是,VB.NET在VB6中執行了一個不合時宜的規定,這是合法的。然而,當你使用線程時,它會崩潰。你會得到另一個表單對象自動創建,一個不可見,因爲它的Show()方法從來沒有被調用過。否則,因爲線程沒有抽取消息循環而死掉。

您需要將對用戶正在查看的實際表單對象的引用傳遞給工作者類。 Me在Form1代碼中的值。您還必須使用Control.Invoke,因爲從另一個線程更新控件是不合法的。我建議你發一個事件來代替Form1可以訂閱的事件,這樣你的工作者類就不會感染UI的實現細節。

+0

我有點理解你說什麼,但我認爲,因爲我在form1中添加了處理程序,並且在form1中添加了它將實際發生的事件,而不是創建新的form1對象。 發生以下代碼的Addlog函數是一個模塊。 Form1.tbLogs.AppendText(s&vbCrLf) 所以當你說我需要傳遞一個對實際表單對象的引用時,我該怎麼做? 昨天我在事件中嘗試過,因爲它的形式是做me.tblogs.appendtext,但它有相同的效果。 – JoshF 2010-09-24 20:37:40

+0

這是一個非常基本的面向對象問題,很難讓你在評論框中加快速度。 Q&D修補程序是使用Application.OpenForms(0)代替,這是對創建的第一個窗體的引用。可能是Form1。 – 2010-09-24 20:43:51

+0

我會試一試,謝謝Hans。這是否解釋了爲什麼它在10秒後顯示在文本框中? 我的意思是如果我正確地讀你,它根本不會更新,因爲它會完全與不同的對象進行交互。 – JoshF 2010-09-24 20:45:07

1

幾點建議:

  1. 首先使其無緒的工作(諷刺你把自己的。業餘愛好者,事實上,我只是在我過了'業餘'階段才學會這樣做)
  2. 不要嘗試從後臺線程更新GUI控件。這在windows中是被禁止的。我不確定那是什麼你這樣做(沒有VB大師),但它確實看起來像。
  3. 使用.net BackgroundWorker類。它具有內置功能,可以從後臺線程回到主線程。這並不完美,但一個好的開始。
1

你讓我指出了正確的方向。感謝Hans。這是我的解決方案:

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更新原始表單上的日誌。

問題是我張貼原始帖子作爲「未註冊用戶」,所以現在我試圖找出一種方式來說問題得到了回答。再次感謝你的幫助!

相關問題