Cross-Threaded ComboBox???

Alrighty...

I feel like I am losing my mind. I have programmed in VBA for over a decade. Custom Access Apps, Word Code, etc... I am switching over to VB.Net on some work projects, and I am hitting the WEIRDEST brick wall I have ever hit.

In my MAIN FORM... I have a combobox called cmb_References

All I want to do is ADD and CLEAR items from this combobox... but I can't !

I keep getting some error saying:

System.InvalidOperationException was caught
Message="Cross-thread operation not valid: Control 'cmb_References' accessed from a thread other than the thread it was created on."
Source="System.Windows.Forms"
StackTrace:
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.SendMessage(Int32 msg, Int32 wparam, Int32 lparam)
at System.Windows.Forms.ComboBox.NativeClear()
at System.Windows.Forms.ComboBox.ObjectCollection.ClearInternal()
at System.Windows.Forms.ComboBox.ObjectCollection.Clear()
at EngGraphicsTool.frmAttachmentTool.UpdateAttachmentList(String TargetLogicalName, Boolean LeaveThis) in C:\My Documents\Visual Studio 2005\Projects\Engineering Graphics Tool\Engineering Graphics Tool\Forms\frmAttachmentTool.vb:line 499

What the bloody heck is going on !

Here is the Sub I am trying to write...

Public Sub UpdateAttachmentList(Optional ByVal TargetLogicalName As String = "BLANK", Optional ByVal LeaveThis As Boolean = True)

Try

Dim MyCmb As ComboBox

Dim counter As Integer, EntryText As String

Dim PageType As String, PageNumber As Integer, NumberOfAttachments As Integer

Dim MyActiveModel As ModelReference, MyAttachments As Attachments, MyAttachment As Attachment

' System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False

MyActiveModel = MyAMR

MyAttachments = MyActiveModel.Attachments

MyCmb = Me.cmb_References

MyCmb.Items.Clear()

NumberOfAttachments = IIf((LeaveThis = True), MyAttachments.Count, MyAttachments.Count - 1)

For counter = 0 To NumberOfAttachments - 1

MyAttachment = MyAttachments.Item(counter + 1)

PageType = LogicalName_to_PageType(MyAttachment.LogicalName)

PageNumber = LogicalName_to_PageNumber(MyAttachment.LogicalName)

EntryText = PageType + " Type, Page #: " + CStr(PageNumber)

MyCmb.Items.Add(EntryText)

Next counter

Catch ex As Exception

Select Case Err.Number

Case 5 ' Cross-thread operation not valid: Control 'cmb_References' accessed from a thread other than the thread it was created on.

End Select

End Try

End Sub

Now for those of you following along... This is an application for interacting with Microstation v8. That is the MyAttachment and such... All I want to make happen is the MyCmb.Items.Add and MyCmb.Items.Clear() and I can't for the life of me figure this out.

My background is mainly with PHP, VBA, Java ( and -script )... I have never had to deal with Threads ! Confused... please help! I have searched across this message board and found nothing to help ! I see alot about Invoking and Delegates, but nothing about simply adding an item or clearing all items from a simple combobox... ! ! ! ! !

HELP!




Answer this question

Cross-Threaded ComboBox???

  • boyapati

    Hello RandomBlink

    As a Guess, you are running procedures on threads other than the one that the containing form is on. This is usually done so that the interface remains responsive even during long running processes.

    You can not update display data from a thread other than the thread that the dsplay control is contained on. On of the easiest ways to accomplish this in Net 2005 is to use the background worker.

    Here's an example:

    Imports system.ComponentModel

    Public Class Form1

    Private WithEvents MyThread As BackgroundWorker

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    MyThread = New BackgroundWorker

    End Sub

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

    Dim Myvalue As String = "Hello World"

    If Not MyThread.IsBusy Then

    MyThread.RunWorkerAsync(MyValue)

    End If

    End Sub

    Private Sub MyThread_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles MyThread.DoWork

    Dim TestString As String = e.Argument

    '------------------------------------

    'call long running procedure here

    '------------------------------------

    e.Result = TestString

    End Sub

    Private Sub MyThread_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles MyThread.RunWorkerCompleted

    '-----------------------

    'return value from the

    'backgrounder thread

    'now you can access the

    'display thread

    '-----------------------

    MsgBox(e.Result)

    End Sub

    End Class

    Later :)

    Ibrahim


  • Ruurd Boeke

    Hi

    As already stated, it sounds like the UpdateAttachmentList method is running on a thread that differs from the thread that was used to create the combo box. This means that anytime you interact with the combo box, you need to coersce the other thread to run your code for you.

    The simplest way to do this is to use the Invoke method of the control and pass it a delegate (a callback to a method). When the callback is invoked it will be running on the appropriate thread and your current thread will be suspended.

    Lets try a simple example of how you might architect your solution ...

    private delegate sub fn1(of T)(byval arg as T) : private delegate sub fn2(of T, T1)(byval arg1 as T, byval arg2 as T1)

    private shared sub clearcombobox(byval cb as combobox)

    if cb.invokerequired then

    cb.invoke(new fn1(of combobox)(addressof clearcombobox), cb)

    else : cb.items.clear

    end if

    end sub

    private shared sub addcomboitem(byval cb as combobox, byval txt as string)

    if cb.invokerequired then

    cb.invoke(new fn2(of combobox, string)(addressof addcomboitem), cb, txt)

    else : cb.items.add(txt)

    end if

    end sub

    now instead of interacting direct with the combo box in your updateattachmentlist method, you'd call the clearcombobox method instead. When you want to add to the combo box, call the addcomboitem method and let it do the work for you.

    Hope this helps, but if you need some further help, please le me know.

    Richard


  • Gloria123

    Hi,

     Is cmb_References on another FORM

     If so remove the Me. reference and replace it with the relevant location or try leaving it out....

     

    I'm suprised the line>>

    Dim myCmb As ComboBox 'isn't giving a squiggly line under the word ComboBox

    Have you got an Imports line in your code

     

     

    Dim myCmd As System.Windows.Forms.ComboBox

    myCmb=ComboBox1

    myCmb.Items.Add("Hello") ' works for me and so does.>>

    Dim myStr As String = "there!!"

    myCmb.Items.Add(myStr)

    ''''myCmb.Items.Clear()   '   This line works too if you take the 1st four comment marks out. ;-) 

     

     

     

    Regards,

    S_DS

     



  • xulei

    You cannot call UI methods and properties directly from another thread than the one the form was created on. You need to use Invoke to marshall the call to the control's thread.

    (If you're using .Net 2.0, consider using the BackgroundWorker class and communicate with the UI via the ProgressChanged event).

    http://msdn2.microsoft.com/en-us/library/b2zk6580(vs.80).aspx

    Something which may also be useful reading.

    http://www.informit.com/guides/content.asp g=dotnet&seqNum=194&rl=1

    If you are using just simple forms and not specifically using different threads then the following works and should mention anything about thread.   It basically sets up a listbox on one form and allows you to create a different form within the same application and as long as you have a reference to the original form/control and your one the same thread then you can modify the listbox items without a problem,

    Public Class Form1
      
        Private Sub CreateASecondForm(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim x As New form2
            x.Form1Var = Me '//Reference to this form
            x.Show()
        End Sub

        Private Sub BtnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnAdd.Click
            Me.ListBox1.Items.Add("abc")
        End Sub
    End Class

     

    Public Class Form2
        Public Form1Var As Form1

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            If Form1Var IsNot Nothing Then
                Form1Var.ListBox1.Items.Clear()
            End If
        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            If Form1Var IsNot Nothing Then
                Form1Var.ListBox1.Items.Add("xyz")
            End If
        End Sub
    End Class


  • Sushisource

    Additional Help ( heck, you offered! )

    I actually do.

    Two questions...

    #1) The program now hangs once it has entered the clearComboBox function... or the addComboBoxItem functions and does the Invoke option. I can't figure out why

    #2) I want to add column'd items to the ComboBox in question. I want to pass an itemID as well as an ITEM. That way when the user selects an item from the list I can pass the itemID to an appropriate function. However, columns are no longer part of the ComboBox as I can tell. Do you know how to do this



  • Cross-Threaded ComboBox???