Results 1 to 10 of 10

Thread: accesing controls from a thread (safely)

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Jun 2008
    Location
    Bweeng, Ireland
    Posts
    69

    Question accesing controls from a thread (safely)

    Hi,

    In short this is what I have 2 forms formA and formB. Button on formA opens formB. FormB has a progress bar and a thread. The thread runs some code that creates a report. That code must access controls on formA e.g.

    Code:
    if formA.checkbox1.checked then
    ..
    end if
    In VB6 this was simply but I can't seem to figure out how to use invoke to do this.

    I would greatly appreciate a detailed answer as I'm under pressure to get this work done.

    Jim

  2. #2
    I'm about to be a PowerPoster! kleinma's Avatar
    Join Date
    Nov 2001
    Location
    NJ - USA (Near NYC)
    Posts
    23,373

    Re: accesing controls from a thread (safely)

    You didn't state the version of .NET you use, so its hard to give you a solid answer.

    You could read over this
    http://msdn.microsoft.com/en-us/libr...28(VS.80).aspx

    If you are using .NET 2.0 or above, then you could also look at using the BackgroundWorker, which automatically handles marshalling between threads.


    Also, just a side note, but using your email address as your user name is probably just asking to get tons of spam as bots crawl these pages looking for email addresses.

  3. #3

    Thread Starter
    Lively Member
    Join Date
    Jun 2008
    Location
    Bweeng, Ireland
    Posts
    69

    Re: accesing controls from a thread (safely)

    Hi,

    First yep probably wasn't a good idea to use my email but its incomplete anyways.

    I use .NET 2. I have tried the back ground worker but as soon as I call the my class that refers to my main form I get the thread error at formA.checkbox1.checked

    Jim

  4. #4
    I'm about to be a PowerPoster! kleinma's Avatar
    Join Date
    Nov 2001
    Location
    NJ - USA (Near NYC)
    Posts
    23,373

    Re: accesing controls from a thread (safely)

    when using the BGW, where are you trying to access your control from? Which routine? You can't access controls in the DoWork routine, that routine is ONLY for the background thread. However from that routine you can call reportprogress on the BGW, which will fire an event in the main UI thread, and there you CAN access UI controls like checkboxes.

  5. #5

    Thread Starter
    Lively Member
    Join Date
    Jun 2008
    Location
    Bweeng, Ireland
    Posts
    69

    Re: accesing controls from a thread (safely)

    Hi,

    This is my do work

    Code:
    Private Sub bgwProcessingReport_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwProcessingReport.DoWork
            ' Run report function
            ProcessReport()
    End Sub
    In side the Process Report I have

    Code:
                    With Me
                        Select Case True
                            Case .rbReportJourneySummary.Checked
                                ' I get the error here.
                                frmActiveReportViewer.FillTreeView(mySQLDataSet) 
                                JourneySummarySetup(mySQLDataSet)
    Also in journey summary setup, I have

    Code:
                With frmDiscoveryII ' I get the error here as well
                    Select Case True
                        Case .rbDateToday.Checked
                            With CType(ShowReport, rptJourneySummary)
                                .txtStartRange.Text = Format(Now(), "dddd, dd MMMM yyyy").ToString
                                .txtEndRange.Text = Format(Now(), "dddd, dd MMMM yyyy").ToString
                            End With
                        Case .rbDateYesterday.Checked
                            With CType(ShowReport, rptJourneySummary)
                                .txtStartRange.Text = Format(DateAdd(DateInterval.Day, -1, Now()), "dddd, dd MMMM yyyy").ToString
                                .txtEndRange.Text = Format(DateAdd(DateInterval.Day, -1, Now()), "dddd, dd MMMM yyyy").ToString
                            End With

    This is the error.

    An error occurred creating the form. See Exception.InnerException for details. The error is: ActiveX control '35f36212-43a6-48fa-8a6a-af558abe7030' cannot be instantiated because the current thread is not in a single-threaded apartment.

    Thanks,
    Jim

  6. #6
    Addicted Member
    Join Date
    Mar 2008
    Posts
    143

    Re: accesing controls from a thread (safely)

    Try creating a delegate and call 'Invoke'...

    Code:
    'This goes outside at the top of your form class code
    Public Delegate Sub InvokeFillTreeViewDelgate(ByVal o As Object)
    ...
    
    With Me
        Select Case True
            Case .rbReportJourneySummary.Checked
                ''replace frmActiveReportViewer.FillTreeView(mySQLDataSet) with
                Invoke(New InvokeFillTreeViewDelgate(AddressOf FillTreeView), mySQLDataSet)
    Shut up and eat your banana!

  7. #7

    Thread Starter
    Lively Member
    Join Date
    Jun 2008
    Location
    Bweeng, Ireland
    Posts
    69

    Question Re: accesing controls from a thread (safely)

    Hi Michael,

    That didn't work for me I still got the following error.

    An error occurred creating the form. See Exception.InnerException for details. The error is: ActiveX control '35f36212-43a6-48fa-8a6a-af558abe7030' cannot be instantiated because the current thread is not in a single-threaded apartment.

    I had to make a couple of changes to your code as the compiler were reporting an error.

    Code:
    Public Delegate Sub InvokeFillTreeViewDelgate(ByVal objD As DataSet)
    Code:
                        Select Case True
                            Case .rbReportJourneySummary.Checked
                                ' Fill Tree View
                                Invoke(New InvokeFillTreeViewDelgate(AddressOf frmActiveReportViewer.FillTreeView), mySQLDataSet)
                                'frmActiveReportViewer.FillTreeView(mySQLDataSet)
                                JourneySummarySetup(mySQLDataSet)
    This is the code for Fill Tree View.

    Code:
    Friend Sub FillTreeView(ByVal MySQLDataReader As DataSet)
            Dim tvAsset As TreeNode = Nothing, tvDate As TreeNode = Nothing
            Dim intX As Integer
            Try
                If tcReport.TabPages.Count = 1 Then Exit Try
    
                With MySQLDataReader.Tables(0)
                    tvReports.Nodes.Clear()
                    For intX = 0 To .Rows.Count - 1
                        ' If Journey is Private and either Private or Limited Private is ticked then do not show
                        If MySQLDataReader.Tables(0).Rows(intX).Item("Type") = "P" And (LoginUsername.JourneyType(1) = "0" Or LoginUsername.JourneyType(2) = "1") Then
                            ' do not show
                        Else
                            If tvReports.Nodes.Count = 0 Then
                                tvAsset = tvReports.Nodes.Add(.Rows(intX).Item("Primary Asset"))
                                tvDate = tvAsset.Nodes.Add(Format(GMTtoUserLocalTime(.Rows(intX).Item("Start")), "dddd, dd MMM, HH:mm") & " - " & Format(GMTtoUserLocalTime(.Rows(intX).Item("End")), "dddd, dd MMM, HH:mm") & " (" & ConvertSeconds(DateDiff(DateInterval.Second, GMTtoUserLocalTime(.Rows(intX).Item("Start")), GMTtoUserLocalTime(.Rows(intX).Item("End")))) & ")")
                                tvDate.Tag = .Rows(intX).Item("JourneyID")
                            Else
                                If tvAsset.Text.ToString = MySQLDataReader.Tables(0).Rows(intX).Item("Primary Asset").ToString Then
                                    tvDate = tvAsset.Nodes.Add(Format(GMTtoUserLocalTime(.Rows(intX).Item("Start")), "dddd, dd MMM, HH:mm") & " - " & Format(GMTtoUserLocalTime(.Rows(intX).Item("End")), "dddd, dd MMM, HH:mm") & " (" & ConvertSeconds(DateDiff(DateInterval.Second, GMTtoUserLocalTime(.Rows(intX).Item("Start")), GMTtoUserLocalTime(.Rows(intX).Item("End")))) & ")")
                                    tvDate.Tag = .Rows(intX).Item("JourneyID")
                                Else
                                    tvAsset = tvReports.Nodes.Add(.Rows(intX).Item("Primary Asset").ToString)
                                    tvDate = tvAsset.Nodes.Add(Format(GMTtoUserLocalTime(.Rows(intX).Item("Start")), "dddd, dd MMM, HH:mm") & " - " & Format(GMTtoUserLocalTime(.Rows(intX).Item("End")), "dddd, dd MMM, HH:mm") & " (" & ConvertSeconds(DateDiff(DateInterval.Second, GMTtoUserLocalTime(.Rows(intX).Item("Start")), GMTtoUserLocalTime(.Rows(intX).Item("End")))) & ")")
                                    tvDate.Tag = .Rows(intX).Item("JourneyID")
                                End If
                            End If
                        End If
                    Next
                    tvReports.ExpandAll()
                End With
            Catch ex As Exception
                MsgBox("Error generating Map Replay list", MsgBoxStyle.Exclamation, "Error")
            End Try
        End Sub
    I'm not to worried about this part, I can make the mySQLDataSet a public variable and access it once the form opens up later on.

    My main problem comes from this code which is called via ProcessReport

    Code:
                With frmDiscoveryII
                    Select Case True
                        Case .rbDateToday.Checked
                            With CType(ShowReport, rptJourneySummary)
                                .txtStartRange.Text = Format(Now(), "dddd, dd MMMM yyyy").ToString
                                .txtEndRange.Text = Format(Now(), "dddd, dd MMMM yyyy").ToString
                            End With
    I know accessing forms from other forms in VB6 was not "safe" but it was a lot easier then this!

    Thanks for all the help.

    Jim

  8. #8

    Thread Starter
    Lively Member
    Join Date
    Jun 2008
    Location
    Bweeng, Ireland
    Posts
    69

    Question Re: accesing controls from a thread (safely)

    Hi Michael,

    That didn't work for me I still got the following error.

    An error occurred creating the form. See Exception.InnerException for details. The error is: ActiveX control '35f36212-43a6-48fa-8a6a-af558abe7030' cannot be instantiated because the current thread is not in a single-threaded apartment.

    I had to make a couple of changes to your code as the compiler were reporting an error.

    Code:
    Public Delegate Sub InvokeFillTreeViewDelgate(ByVal objD As DataSet)
    Code:
                        Select Case True
                            Case .rbReportJourneySummary.Checked
                                ' Fill Tree View
                                Invoke(New InvokeFillTreeViewDelgate(AddressOf frmActiveReportViewer.FillTreeView), mySQLDataSet)
                                'frmActiveReportViewer.FillTreeView(mySQLDataSet)
                                JourneySummarySetup(mySQLDataSet)
    This is the code for Fill Tree View.

    Code:
    Friend Sub FillTreeView(ByVal MySQLDataReader As DataSet)
            Dim tvAsset As TreeNode = Nothing, tvDate As TreeNode = Nothing
            Dim intX As Integer
            Try
                If tcReport.TabPages.Count = 1 Then Exit Try
    
                With MySQLDataReader.Tables(0)
                    tvReports.Nodes.Clear()
                    For intX = 0 To .Rows.Count - 1
                        ' If Journey is Private and either Private or Limited Private is ticked then do not show
                        If MySQLDataReader.Tables(0).Rows(intX).Item("Type") = "P" And (LoginUsername.JourneyType(1) = "0" Or LoginUsername.JourneyType(2) = "1") Then
                            ' do not show
                        Else
                            If tvReports.Nodes.Count = 0 Then
                                tvAsset = tvReports.Nodes.Add(.Rows(intX).Item("Primary Asset"))
                                tvDate = tvAsset.Nodes.Add(Format(GMTtoUserLocalTime(.Rows(intX).Item("Start")), "dddd, dd MMM, HH:mm") & " - " & Format(GMTtoUserLocalTime(.Rows(intX).Item("End")), "dddd, dd MMM, HH:mm") & " (" & ConvertSeconds(DateDiff(DateInterval.Second, GMTtoUserLocalTime(.Rows(intX).Item("Start")), GMTtoUserLocalTime(.Rows(intX).Item("End")))) & ")")
                                tvDate.Tag = .Rows(intX).Item("JourneyID")
                            Else
                                If tvAsset.Text.ToString = MySQLDataReader.Tables(0).Rows(intX).Item("Primary Asset").ToString Then
                                    tvDate = tvAsset.Nodes.Add(Format(GMTtoUserLocalTime(.Rows(intX).Item("Start")), "dddd, dd MMM, HH:mm") & " - " & Format(GMTtoUserLocalTime(.Rows(intX).Item("End")), "dddd, dd MMM, HH:mm") & " (" & ConvertSeconds(DateDiff(DateInterval.Second, GMTtoUserLocalTime(.Rows(intX).Item("Start")), GMTtoUserLocalTime(.Rows(intX).Item("End")))) & ")")
                                    tvDate.Tag = .Rows(intX).Item("JourneyID")
                                Else
                                    tvAsset = tvReports.Nodes.Add(.Rows(intX).Item("Primary Asset").ToString)
                                    tvDate = tvAsset.Nodes.Add(Format(GMTtoUserLocalTime(.Rows(intX).Item("Start")), "dddd, dd MMM, HH:mm") & " - " & Format(GMTtoUserLocalTime(.Rows(intX).Item("End")), "dddd, dd MMM, HH:mm") & " (" & ConvertSeconds(DateDiff(DateInterval.Second, GMTtoUserLocalTime(.Rows(intX).Item("Start")), GMTtoUserLocalTime(.Rows(intX).Item("End")))) & ")")
                                    tvDate.Tag = .Rows(intX).Item("JourneyID")
                                End If
                            End If
                        End If
                    Next
                    tvReports.ExpandAll()
                End With
            Catch ex As Exception
                MsgBox("Error generating Map Replay list", MsgBoxStyle.Exclamation, "Error")
            End Try
        End Sub
    I'm not to worried about this part, I can make the mySQLDataSet a public variable and access it once the form opens up later on.

    My main problem comes from this code which is called via ProcessReport

    Code:
                With frmDiscoveryII
                    Select Case True
                        Case .rbDateToday.Checked
                            With CType(ShowReport, rptJourneySummary)
                                .txtStartRange.Text = Format(Now(), "dddd, dd MMMM yyyy").ToString
                                .txtEndRange.Text = Format(Now(), "dddd, dd MMMM yyyy").ToString
                            End With
    I know accessing forms from other forms in VB6 was not "safe" but it was a lot easier then this!

    Thanks for all the help.

    Jim

  9. #9
    Raging swede Atheist's Avatar
    Join Date
    Aug 2005
    Location
    Sweden
    Posts
    8,018

    Re: accesing controls from a thread (safely)

    Have you tried calling the backgroundworders ReportProgress method, which raises the ProgressChanged event, and execute the code that accesses the controls in its eventhandler? Thats how it should be done using BackgroundWorkers, which kleinma pointed out.
    Rate posts that helped you. I do not reply to PM's with coding questions.
    How to Get Your Questions Answered
    Current project: tunaOS
    Me on.. BitBucket, Google Code, Github (pretty empty)

  10. #10

    Thread Starter
    Lively Member
    Join Date
    Jun 2008
    Location
    Bweeng, Ireland
    Posts
    69

    Question Re: accesing controls from a thread (safely)

    Hi,

    I have tried doing this.

    Code:
        Private Sub bgwProcessingReport_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwProcessingReport.DoWork
            bgwProcessingReport.ReportProgress(1)
        End Sub
    
        Private Sub bgwProcessingReport_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgwProcessingReport.ProgressChanged
            ' Run report function
            ProcessReport()
        End Sub
    The reason I want to run Process Report in a back ground thread is that it can cause the form to go "WHITE". If I do Process Report in Progress Changed event then it results in the same issue as it seems that event is ran in the main thread of the form which is basically the same as running it without any threads.

    I should add that I've tried

    Code:
    Dim f As Form1 = My.Application.OpenForms("Form1")
    But my tree view selected node throws up the same error - single threading etc.

    Thanks,
    Jim

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width