Community
Showing results for 
Search instead for 
Do you mean 
Reply

Cross-thread operation not valid

Accepted Solution Solved
Copper Contributor
Posts: 13
Country: USA
Accepted Solution

Cross-thread operation not valid

So I'm using  System.Threading.Timer to  check for remote updates and then do a Refresh of the UI if there is an update.

 

However when i try this:

 

application.RefreshView(typeof(Act.UI.IContactDetailView).FullName);

 

I get the following exception:

 

System.InvalidOperationException was unhandled
  Message="Cross-thread operation not valid: Control 'textBox' 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.ToolTip.GetMinTOOLINFO(Control ctl)
       at System.Windows.Forms.ToolTip.GetTOOLINFO(Control ctl, String caption, Boolean& allocatedString)
       at System.Windows.Forms.ToolTip.SetToolTipInternal(Control control, TipInfo info)
       at System.Windows.Forms.ToolTip.SetToolTip(Control control, String caption)
       at Act.Shared.Windows.Forms.GenericComboBox.set_ToolTip(String value)
       at Act.Shared.Windows.Forms.GenericComboBox.get_Text()
       at Act.Shared.Windows.Forms.EditableListComboBox.get_Text()
       at Act.Shared.Windows.Forms.EditableListComboBox.get_Value()
       at Act.UI.PickList.PickListEditor.ValidateEdit()
       at Act.UI.PickList.MutableEntityFieldEditor.ValidateEdit()
       at Act.UI.Utilities.DetailViewValidator.control_Validating(Object sender, CancelEventArgs e)
       at Act.UI.Utilities.DetailViewValidator.ValidateControls(Boolean showErrorDialog)
       at Act.UI.Utilities.DetailViewValidator.HasErrors(Boolean showErrorDialog)
       at Act.UI.ContactViews.ContactDetailView.BeforeChangeContacts(Object sender, CancelEventArgs e)
       at Act.UI.ContactViews.ContactDetailView.RefreshView()
       at Act.UI.ActApplication.RefreshView(String viewInterfaceName)
       at Mediagistic.Multiplex.Services.Sync.Plugin.ActPlugin.CheckForUpdates(Object stateInfo)
       at System.Threading._TimerCallback.TimerCallback_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading._TimerCallback.PerformTimerCallback(Object state)

 

I understand the exception, but do not understand how to fix it.

 

How do I have the main thread the refresh when necessary?


Accepted Solutions
Solution
Accepted by topic author apemberton
‎09-25-2015 03:20 AM
Copper Contributor
Posts: 13
Country: USA

Re: Cross-thread operation not valid

The solution to the problem was merely to use ActApplication.Invoke with a delegate that called refresh

View solution in original post


All Replies
Bronze Super Contributor
Posts: 1,231
Country: USA

Re: Cross-thread operation not valid

[ Edited ]

You need to create a background worker thread then report the changes back to the main thread. This is espacially try when trying to change UI on the main thread.  I hacked this out of a project I am working on.  It may still help.

 

' Step 1:  create a method for the worker thread to perform

 

  Private Sub BackgroundWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)

        Try
            '-------------------------------------------------------------
            ' Count the total number of histories in the Current Database
            '-------------------------------------------------------------
            Dim ds As DataSet = Durkin.Common.Data.SQL.getSQLDataSet(Me.HostFramework, Common.Classes.DurkinEnums.EntityDataMode.ACTOLEDB2, "SELECT COUNT(*) AS COUNT  FROM HISTORY")

            '-------------------------------------------------------------
            ' Create a UserState object to pass back to the main thread
            ' "ObjectNamed" is a class I created to hold any type of object
            ' and also be able to give it a name
            '-------------------------------------------------------------
            Dim namedObj As Durkin.Common.Utilities.Objects.ObjectNamed = New Durkin.Common.Utilities.Objects.ObjectNamed(DirectCast(DirectCast(ds.Tables(0).Rows(0).Item("Count"), System.Object), System.Int32), "histories Count = ")

            '-------------------------------------------------------------
            ' Report progress here
            '-------------------------------------------------------------
            DirectCast(sender, BackgroundWorker).ReportProgress(0, namedObj)

        Catch ex As Exception
            Durkin.Common.Utilities.LogError.Write(ex, "BackgroundWorker_DoWork")

        End Try
    End Sub

 

  ' Step 2: create the method for the worker thread to report its progress

    Private Sub BackgroundWorker_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
        Try

            '-------------------------------------------------------------
            ' Get the TEXT and the VALUE from the e.UserState object ( the ObjectNamed class  we created above)
            ' and update our label on the form
            '-------------------------------------------------------------
            Me.Label1.Text = DirectCast(e.UserState, Durkin.Common.Utilities.Objects.ObjectNamed).Text + DirectCast(e.UserState, Durkin.Common.Utilities.Objects.ObjectNamed).Value.ToString("n0")

        Catch ex As Exception
            Durkin.Common.Utilities.LogError.Write(ex, "BackgroundWorker_ProgressChanged")

        End Try

    End Sub

 

Now all we need to do it to create the worker thread  and run it

 

   Dim bw As BackgroundWorker = New BackgroundWorker()
   bw.WorkerReportsProgress = True
   AddHandler bw.ProgressChanged, AddressOf BackgroundWorker_ProgressChanged
   AddHandler bw.DoWork, AddressOf BackgroundWorker_DoWork
   bw.RunWorkerAsync()

 

I am also including my ObjectNamed class from my Utilitiy tools that is used as the UserState object above.

 Public Class ObjectNamed

        Dim mValue As Object
        Dim mText As String
        Dim mImageBytes As Byte()

        Public Sub New(ByVal value As Object, ByVal text As String)
            Me.New(value, text, Nothing)
        End Sub

        Public Sub New(ByVal value As Object, ByVal text As String, ByVal imageBytes As Byte())
            mValue = value
            mText = Text
            mImageBytes = imageBytes
        End Sub

        Public Property Value() As Object
            Get
                Return mValue
            End Get
            Set(ByVal Value As Object)
                mValue = Value
            End Set
        End Property
        Public Property Text() As String
            Get
                Return mText
            End Get
            Set(ByVal Value As String)
                mText = Value
            End Set
        End Property

        Public Property Name() As String ' For backwards compatibility with our custom controls
            Get
                Return mText
            End Get
            Set(ByVal Value As String)
                mText = Value
            End Set
        End Property

        Public Property ImageBytes() As Byte()
            Get
                Return mImageBytes
            End Get
            Set(ByVal Value As Byte())
                mImageBytes = Value
            End Set
        End Property
    End Class

 

 

I hope this help. It took me a few hours to get my head around it but once it clicks its not the complicated.

-- Jim Durkin

Solution
Accepted by topic author apemberton
‎09-25-2015 03:20 AM
Copper Contributor
Posts: 13
Country: USA

Re: Cross-thread operation not valid

The solution to the problem was merely to use ActApplication.Invoke with a delegate that called refresh

Bronze Super Contributor
Posts: 1,231
Country: USA

Re: Cross-thread operation not valid

Great to know. Thanks!

 

-- Jim Durkin

Copper Super Contributor
Posts: 112
Country: Australia

Re: Cross-thread operation not valid

wow i have not seen that many "DirectCast" statements except in reflector code Smiley Happy

Tuned Listener
Posts: 10
Country: Guatemala

Re: Cross-thread operation not valid

Apembenton:

 

Can you provide a sample of your solution ?

 

I been working in a plugin that reads information from a socket connection, so have several async operations.

 

When the control is back i end with code in a callback function, and now need to locate contacts using the act framework and then refresh the gui with the current contact. This is causing ACT to just show "An error was detected. Please close ACT"

 

here is chunk of my code

 

    class Plugin : IPlugin
    {

        private ActApplication application;
        private ActFramework framework;
        Server  tcpmg = null;

        //avoid cross-thread problems
        IContactView icv = null;
        UIContactManager uicm = null;

        //constructor

        void IPlugin.OnLoad(ActApplication application)
        {       
            // hold on to the application
            this.application = application;
            this.framework = application.ActFramework;
        }

        private void Application_AfterLogon(object sender, EventArgs e)
        {
                //store objects to be used in the call back code
                icv = this.application.UIContactManager.GetDefaultContactView();
                uicm = this.application.UIContactManager;
       }

        //callback from the tcp socket management, its ex

        void tcpmg_DataReceived(object sender, EventArgs e)
        {
               /*   code to create criteria objects ..........................*/

                ContactLookup ctlook = this.application.ActFramework.Lookups.LookupContactsReplace(lCriteria, true,true);
              
                ctclist = ctlook.GetContacts(null);

              //refresh lookup and show detail window

               this.application.ApplicationState.SetCurrentContactList(ctclist,null);

               /* the following lines only works if the current view is contact detail or contact list and also works only one time then

                    ACT just hang.    If the user is in activities or opportunies ACT just show a window that states about an error and

                    ask to close ACT */
                        uicm.ShowContact(ctclist[0]);              
                        uicm.ShowDetailView();              
                        uicm.SetFocus();              
                        
                        this.application.RefreshView("Act.UI.IContactDetailView");
                   


        }

 

Nickel Contributor
Posts: 175
Country: USA

Re: Cross-thread operation not valid

WIth Apembenton's reply, and your code, you have all you need to get your solution to work.

 

What you need to do is create a delegate sub routine (not sure how to do this in C#, I'm a VB person), and move all your current callback code into it. Then call that delegate using "ActApplication.Invoke" in your callback method. 

 

 

To give a synapsis of what is happening: Your socket connection is on a different thread from the UI. For others unfamiliar as to why, it is because a socket listening for incoming connections is a blocking call (i.e. hangs the application), so it must be on a different thread to avoid that. When your async method triggers the callback, it's still on that different thread. For all .NET applications, any UI calls must originate from the UI thread! That is why, in your callback method, you must invoke the delegate sub using ActApplication.Invoke. This is basically having something on UI thread call your subroutine. You have to use a delegate because of crossing threads. This process is also called marshalling (specifically cross-thread marshalling).

 

I hope this helps.