2008-11-07 16 views
0

我有一個Windows窗體應用程序,它使用Shared類來容納應用程序的所有公共對象。 settings類擁有一組定期處理事物的對象,然後有一些有趣的事情,他們需要提醒主窗體並更新它。強制多線程VB.NET類在單個窗體上顯示結果

我目前正在通過對象上的事件來完成此任務,並且在創建每個對象時,我添加一個EventHandler以將事件映射回窗體。但是,我遇到了一些麻煩,表明這些請求並不總是以我的表單的主要副本結尾。例如,我的表單有一個通知托盤圖標,但是當表單捕捉並顯示事件並嘗試顯示氣泡時,不會出現氣泡。但是,如果我修改該代碼以使圖標可見(儘管它已經存在),然後顯示氣泡,則會出現第二個圖標並正確顯示氣泡。

有沒有人碰到過這個?有沒有一種方法可以強制我的所有事件被表單的單個實例捕獲,還是有一種完全不同的方式來處理?如有必要,我可以發佈代碼示例,但我認爲這是一個常見的線程問題。

更多信息:我目前在窗體上的事件處理程序中使用Me.InvokeRequired,並且在這種情況下總是返回FALSE。另外,當我通過這個表單顯示時創建的第二個托盤圖標上沒有上下文菜單,而「真實」圖標卻沒有 - 這是否會影響任何人?

我要把我的頭髮拉出來!這不可能是那麼難!

解決方案:感謝nobugz提供的線索,它將我引向我正在使用的代碼(雖然我不禁想着有更好的方法來做到這一點,但我現在正在使用該代碼)。我添加了一個私有的布爾變量叫「值isPrimary」的形式,並添加以下代碼添加到窗體構造函數:

Public Sub New() 
     If My.Application.OpenForms(0).Equals(Me) Then 
      Me.IsFirstForm = True 
     End If 
    End Sub 

一旦這個變量被設置和構造完成,它朝向正確的事件處理程序,我以這種方式處理它(CAVEAT:由於我正在尋找的表單是應用程序的主要形式,My.Application.OpenForms(0)得到我所需要的。如果我正在尋找非易失性存儲器的第一個實例,啓動窗體,我不得不遍歷,直到我找到它):

Public Sub EventHandler() 
     If Not IsFirstForm Then 
      Dim f As Form1 = My.Application.OpenForms(0) 
      f.EventHandler() 
      Me.Close() 
     ElseIf InvokeRequired Then 
      Me.Invoke(New HandlerDelegate(AddressOf EventHandler)) 
     Else 
      ' Do your event handling code ' 
     End If 
    End Sub 

首先,它會檢查它是否正確的表格上運行 - 如果不是,則調用右擊形式。然後檢查線程是否正確,如果不是,則調用UI線程。然後它運行事件代碼。我不喜歡這可能是三個電話,但我想不出另一種方式來做到這一點。它似乎工作得很好,雖然它有點麻煩。如果有人有更好的方法來做到這一點,我很樂意聽到它!

再一次,感謝所有的幫助 - 這會讓我瘋狂!

回答

3

我認爲這也是一個線程問題。你在事件處理程序中使用Control.Invoke()嗎? .NET通常在調試應用程序時捕獲違規,但有些情況不能。 NotifyIcon就是其中之一,沒有窗口句柄來檢查線程關聯。 OP變化問題

編輯後:

經典VB.NET陷阱是由它的類型名稱引用窗體實例。像Form1.NotifyIcon1.Something一樣。當您使用線程時,這並不像預期的那樣工作。它將創建一個新的 Form1類的實例,而不是使用現有的實例。這個實例是不可見的(Show()從來沒有被調用過),否則它會作爲一個門圖片死掉,因爲它在沒有抽取消息循環的線程上運行。看到第二個圖標出現是一個死亡的贈品。所以當你知道你正在使用它從一個線程獲取InvokeRequired = False。

您必須使用對現有表單實例的引用。如果這是來之不易(您通常通過「我」作爲參數傳遞給類的構造函數),你可以使用Application.OpenForms:

Dim main As Form1 = CType(Application.OpenForms(0), Form1) 
    if (main.InvokeRequired) 
    ' etc... 
+0

作爲回報,我已經更新了我的問題與我想出了根據您的線索的解決方案。感謝您的轉向! – SqlRyan 2008-11-10 15:34:15

0

使用Control.InvokeRequired,以確定如果你是正確的線程,然後使用Control.Invoke,如果你不是。

0

您應該查看窗體上的Invoke方法的文檔。它將允許您使擁有窗體的線程(它必須這樣做,Windows窗體不是線程安全的)上運行的窗體更新代碼。 喜歡的東西 私人代表小組UpdateStatusDelegate(BYVAL NEWSTATUS作爲字符串)

公共子UpdateStatus(BYVAL NEWSTATUS作爲字符串) 如果Me.InvokeRequired然後 昏暗d作爲新UpdateStatusDelegate(AddressOf UpdateStatus) Me.Invoke(d,新的對象(){} NEWSTATUS) 否則 「更新的形式狀態 結束如果

如果你提供一些示例代碼,我會很樂意提供更有針對性的例子。

在OP後編輯表示他們正在使用InvokeRequired。

在調用InvokeRequired之前,檢查表單句柄是否已經創建,是否有HandleCreated屬性。如果控件當前沒有句柄,則InvokeRequired始終返回false,這意味着代碼不是線程安全的,即使您已經完成了正確的操作。當你發現時更新你的問題。一些示例代碼也會有幫助。

0

在C#中,它看起來是這樣的:

private EventHandler StatusHandler = new EventHandler(eventHandlerCode) 
void eventHandlerCode(object sender, EventArgs e) 
    { 
     if (this.InvokeRequired) 
     { 
      this.Invoke(StatusHandler, sender, e); 
     } 
     else 
     { 
      //do work 
     } 
    }