Sherlock
Sherlock

Reputation: 5627

Exception when editing a control that apparently does not require an Invoke

I am attempting to change the Text property on a control from another thread. I am using this method to do this:

        public static void InvokeIfRequired(this Control control, MethodInvoker action)
        {
            if (control.InvokeRequired)
            {
                try
                {
                    control.Invoke(action);
                }
                catch (ObjectDisposedException) { }
                catch (InvalidAsynchronousStateException) { }
            }
            else
            {
                action();
            }
        }

I am still getting a 'System.InvalidOperationException' exception when using this method. When I break into this, I can see that control.InvokeRequired is false, but it looks like it is in fact being accessed from another thread ??

In the actual error message, it doesn't give me the same of the control that I am trying to invoke. Rather, it gives me the form in which the control resides, stating 'Form1 accessed from a thread other than the thread it was created on'.

I have trying passing the form to the InvokeIfRequired method, but still Form1.InvokeRequired is false and the exception occurs. The control I am trying to invoke was created in the designer.

Here is the code I am using to edit the control:

        private void StartUpdateThreads()  // called from the Form ctor
        {
            new Thread(new ThreadStart(this.TimeUpdater)).Start();
            new Thread(new ThreadStart(this.AccountBalanceUpdater)).Start();
        }

        private void TimeUpdater()
        {
            while (!this.IsDisposed)
            {
                Utilities.InvokeIfRequired(this, (MethodInvoker)(() =>
                    {
                        this.timestampLabel.Text = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss");
                    }));
                Thread.Sleep(250);
            }
        }

Here is the call stack:

    MyApp.exe!MyApp.MyAppToolbar.TimeUpdater.AnonymousMethod__0() Line 133 + 0x41 bytes 
    MyApp.exe!MyApp.Utilities.InvokeIfRequired(System.Windows.Forms.Control control, System.Windows.Forms.MethodInvoker action) Line 34 + 0xb bytes 
    MyApp.exe!MyApp.MyAppToolbar.TimeUpdater() Line 131 + 0x49 bytes

Here is the stack trace:

    at System.Windows.Forms.Control.get_Handle()\r\n   
    at System.Windows.Forms.Control.get_InternalHandle()\r\n   
    at System.Windows.Forms.Control.get_CreateParams()\r\n   
    at System.Windows.Forms.Label.get_CreateParams()\r\n   
    at System.Windows.Forms.Control.SizeFromClientSize(Int32 width, Int32 height)\r\n   
    at System.Windows.Forms.Control.SizeFromClientSize(Size clientSize)\r\n   
    at System.Windows.Forms.Label.GetBordersAndPadding()\r\n   
    at System.Windows.Forms.Label.GetPreferredSizeCore(Size proposedConstraints)\r\n   
    at System.Windows.Forms.Control.GetPreferredSize(Size proposedSize)\r\n   
    at System.Windows.Forms.Label.GetPreferredSize(Size proposedSize)\r\n   
    at System.Windows.Forms.Control.get_PreferredSize()\r\n   
    at System.Windows.Forms.Label.AdjustSize()\r\n   
    at System.Windows.Forms.Label.OnTextChanged(EventArgs e)\r\n   
    at System.Windows.Forms.Control.set_Text(String value)\r\n   
    at System.Windows.Forms.Label.set_Text(String value)\r\n   
    at MyApp.MyAppToolbar.<TimeUpdater>b__0() in PATH\\TO\\FILE.cs:line 133\r\n   
    at MyApp.Utilities.InvokeIfRequired(Control control, MethodInvoker action) in PATH\\TO\\FILE.cs:line 34\r\n  MyApp.MyAppToolbar.TimeUpdater() in PATH\\TO\\FILE.cs:line 131\r\n   
    at System.Threading.ThreadHelper.ThreadStart_Context(Object state)\r\n   
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)\r\n   
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)\r\n   
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)\r\n   
    at System.Threading.ThreadHelper.ThreadStart()

Could someone point me in the right direction here ?

Upvotes: 0

Views: 245

Answers (2)

Vlad
Vlad

Reputation: 35594

Reading the documentation on InvokeRequired you can see that it returns false when the control's handle hasn't been created yet. The documentation states that you need to check the value of IsHandleCreated before.

And there's an issue on the destruction time as well, as Hans states above, so you might want to check if the control is already disposed when inside the correct thread.

Another approach would be to start the background threads when the controls are fully loaded, but again this makes InvokeIfRequired not a general-purpose tool.

Upvotes: 2

terrybozzio
terrybozzio

Reputation: 4532

Hi sherlock i think this will be helpfull http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx

Upvotes: 0

Related Questions