2014-01-31 52 views
1

我正在開發一個VB.Net項目,並試圖從正在後臺運行的線程(必要的惡意)向我的表單添加標籤。從線程向表單添加標籤

我已經創建了全局變量SmallTextLabel(類型:標籤),並已分配了所有我想要的屬性。我現在只是試圖將它添加到我的表單使用Me.Controls.Add(SmallTextLabel)。每次運行此代碼時,都會告訴我必須調用該控件。我在屏幕上尖叫,那正是我想要做的,但它聽着嗎?!

無論如何,我已經搜索了過去4個小時的互聯網,並沒有設法解決這個問題,所以請...任何人都可以在這裏爲我閃耀一些光芒?

這是我目前正在AddSmallLabelControl(SmallTextLabel)調用代碼

修訂例如

Private Delegate Sub AddSmallLabelControlHandler(ByVal test As Label) 
Private Sub AddSmallLabelControl(ByVal test As Label) 
    If Me.InvokeRequired Then 
     Dim d As New AddSmallLabelControlHandler(AddressOf AddSmallLabelControl) 
     Me.Invoke(d, test) 
    Else 
     Me.Controls.Add(test) 
     test.BringToFront() 
    End If 
End Sub 

Private Sub ShowScanConfirmationLabel() 
    Dim SmallTextLabel As New Label 
    Dim TahomaSmall As New Font("Tahoma", 20, FontStyle.Bold) 

    With SmallTextLabel 
     .Height = 40 
     .Width = 312 
     .Location = New Point(3, 121) 
     .BackColor = Color.Green 
     .Text = "Testing" 
     .TextAlign = ContentAlignment.TopCenter 
     .Font = TahomaSmall 
    End With 

    End With 
    Call AddSmallLabelControl(SmallTextLabel) 
End Sub 

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
    Dim MakeLabelsVisibleHandler As New ThreadStart(AddressOf MakeLabelsVisible) 
    Dim timerThread As New Thread(MakeLabelsVisibleHandler) 
    timerThread.Start() 
End Sub 

Private Sub MakeLabelsVisible() 
    ShowScanConfirmationLabel() 
End Sub 
+1

堅持。你在什麼線程初始化標籤? (你在哪裏設置'SmallTextLabel = New Label()'),如果你檢查'Me.SmallTextLabel.InvokeRequired',那麼你應該調用'SmallTextlabel.Invoke(...)',但是當你定義並初始化laben單獨的線程,你可能想檢查'Me.InvokeRequired'而不是... – MrPaulch

+0

我目前正在初始化標籤在調用thread.Dim SmallTextLabel作爲新標籤 –

+2

如果不是Me.SmallTextLabel.InvokeRequired然後拋出新的異常(「你是做錯了,試着用我來代替「) –

回答

2

好吧,我發現了一點,但嚴重inconsitancy在你的代碼,這可能可能會影響你實際上不運行調用...

假設你在工作線程中初始化標籤,然後檢查Me.SmallLabel.InvokeRequired將返回False,因爲它已經在同一個線程上創建。因此,您的程序實際上不會調用Me.Invoke(...)

當您嘗試訪問已在另一個線程上創建的窗體的控件時,應檢查該窗體是否需要調用。你的情況:Me.InvokeRequired

試試這個代碼,而不是:

Private Delegate Sub AddSmallLabelControlHandler(ByVal test As Label) 
Private Sub AddSmallLabelControl(ByVal test As Label) 
    If Me.InvokeRequired Then 
     Dim d As New AddSmallLabelControlHandler(AddressOf AddSmallLabelControl) 
     Me.Invoke(d, test) 
    Else 
     Me.Controls.Add(test) 
     ' If you need .BringToFront use it here: 
     test.BringToFront() 
     ' But like this it is called every time you add a label of course. 
    End If 
End Sub 

編輯: 關於.NET CF

如此看來Invoke表現在Compact Framework的一些獨特的方式。即它預計調用EventHandler沒有別的

來源:MSDN

在我們要重構我們的代碼,這種情況下:

首先,因爲我們必須使用EventHandler,我們需要用它的簽名是EventHandler(Object sender, EventArgs e)

所以當我們需要發送一個新的Label到目標程序,一個簡單的解決方案是將它放入sender並在之後進行投射。

但謹慎的做法是實現您自己的EventArgs類包含一個Control並投出一個。

所以這裏的示例代碼VB變種展示如何調用控件上的CF

' Start the worker thread 
Private Sub InitWorker() 
    Dim trdWorker As New Thread(New ThreadStart(AddressOf WorkerThread)) 
    trdWorker.Start() 
End Sub 

Private Sub WorkerThread() 
    Dim testLabel As New Label() With { 
     .Name = "TestLabel", 
     .Text = "Test Label", 
     .Location = New Point(5, 30) 
    } 
    ' Create your EventArgs containing the control you wish to add 
    Dim e As New AddControlEventArgs(testLabel) 

    ' Create EventHandler 
    Dim ehnd As New EventHandler(AddressOf AddControl) 

    ' Invoke EventHandler with EventArgs (don't need sender as of now) 
    Me.Invoke(ehnd, Nothing, e) 
End Sub 

' Define your custom EventArgs to hold one Control 
Public Class AddControlEventArgs : Inherits EventArgs 
    Public Sub New(p_control As Control) 
     m_control = p_control 
    End Sub 
    Private m_control As Control 
    Public ReadOnly Property Control As Control 
     Get 
      Return m_control 
     End Get 
    End Property 
End Class 

Private Sub AddControl(sender As Object, e As EventArgs) 
    ' Cast EventArgs to your custom EventArgs 
    Dim ec As AddControlEventArgs = DirectCast(e, AddControlEventArgs) 
    Me.Controls.Add(ec.Control) 

    ' Still, do you need this? 
    ec.Control.BringToFront() 
End Sub 

最後的一些注意事項:

  • 我的解決方案從一個在MSDN的源中找到不同一個基本的方法。 MSDN的解決方案存儲消息希望它的調用在類中的中性場,我創建自己的EventArgs持有的消息(在我們的情況下,Control

  • 我沒有CF環境安裝,所以我不能測試解決方案 CF環境。這將是巨大的,有一些反饋,這是否真正解決了問題

  • 你應該compact-framework標記您的文章,作爲對CF的解決方案可以從根本上不同於正常解決方案(通過這一困境證明)

+0

如果你添加一個解釋,說明Jason的代碼出了什麼問題,以及代碼如何以及爲什麼修復它,我很樂意提出你的答案。 –

+0

編輯後。你認爲這種解釋是否足夠? – MrPaulch

+0

感謝您的快速回復,但是遺憾的是,這還沒有讓我走出困境。每次運行代碼時,Me.Invoke(d,test)都會提示消息Control.Invoke必須用於與在單獨的線程上創建的控件進行交互。 :( –

0

通過在調用中移動標籤的創建,代碼運行時沒有問題。感謝@MrPaulch在這個問題上的所有幫助。將提供你最後的評論。

Private Delegate Sub AddSmallLabelControlHandler(ByVal test As String) 
Private Sub AddSmallLabelControl(ByVal test As String) 
    If Me.InvokeRequired Then 
     Dim d As New AddSmallLabelControlHandler(AddressOf AddSmallLabelControl) 
     Me.Invoke(d, test) 
    Else 
     Dim SmallTextLabel As New Label 
     Dim TahomaSmall As New Font("Tahoma", 20, FontStyle.Bold) 

     With SmallTextLabel 
      .Height = 40 
      .Width = 312 
      .Location = New Point(3, 121) 
      .BackColor = Color.Green 
      .Text = test 
      .TextAlign = ContentAlignment.TopCenter 
      .Font = TahomaSmall 
     End With    
     Me.Controls.Add(SmallTextLabel) 
     test.BringToFront() 
    End If 
End Sub 

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
    Dim MakeLabelsVisibleHandler As New ThreadStart(AddressOf MakeLabelsVisible) 
    Dim timerThread As New Thread(MakeLabelsVisibleHandler) 
    timerThread.Start() 
End Sub 

Private Sub MakeLabelsVisible() 
    Call AddSmallLabelControl("This now works") 
End Sub