0

Possible Duplicate:
The calling thread cannot access this object because a different thread owns it

I'm getting this Error in my Serialport program:

The calling thread cannot access this object because a different thread owns it. wpf

I'm having a Timer Function to update my UI and also in a same time User can press any button to communicate with Serial port.

public MainWindow()
{
    InitializeComponent();

    OpenPort();

    Timer aTimer = new System.Timers.Timer();
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
    aTimer.Interval = 5000;
    aTimer.Enabled = true;
}

And here my two functions :

private void OnTimedEvent(object sender, EventArgs e)
{
    CheckStatus(); // this function will update my UI base of information will receive from serial port         
}

private void Processing()
{
    // Do stuff with serial port
}
Community
  • 1
  • 1
PublicAffair
  • 55
  • 1
  • 9

3 Answers3

2

im having a Timer Function to update my UI

Use Dispatcher.Invoke to modify the GUI from some other thread.

In WPF, only the thread that created a DispatcherObject may access that object. For example, a background thread that is spun off from the main UI thread cannot update the contents of a Button that was created on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous.

Habib
  • 219,104
  • 29
  • 407
  • 436
2

The problem is, as your error says, that you launched a thread with a specific function. In your case, when you launch a thread using OnTimedEvent, this functions is running on a different scope from your main application:

enter image description here

That means: whatever you try to access from OnTimedEvent related to your main program or viceversa, will fail, as it's not running on the same scope. What can you do? I use delegations.

At first, you need to know the Context on which your main application is running. So, create a global variable for your main application:

private SynchronizationContext uiContext;

Then, when you launch your application catch the state of the context like this:

public MainWindow()
{
    uiContext = SynchronizationContext.Current;
}

With this, you can know the real context of your application or different functions. So, if you want to know if your OnTimedEvent is running on a different context and delegate it you will do this:

if (SynchronizationContext.Current != uiContext)
{
    uiContext.Post(delegate { EnableMenuAndButtons(o, args); }, null);
}
else
{
    // Do your normal stuff here
}

And with that, you should be able to use variables with your thread and vivecersa.

o and args are my passed variables. I'm using WPF, but you can use this example with regular Windows Forms (but you will have to use it's specific methods). The library you will need is System.Threading.

Clarification: For you, like this:

private void OnTimedEvent(object sender, EventArgs e)
{
    if (SynchronizationContext.Current != uiContext)
    {
        uiContext.Post(delegate { EnableMenuAndButtons(sender, e); }, null);
    }
    else
    {
        // Do your normal stuff here
        CheckStatus(); // this function will update my UI base of information will receive from serial port 
    }
}

}

Sonhja
  • 8,230
  • 20
  • 73
  • 131
0

Note that with System.Timers.Timer the tick event is generated in another thread different from the main UI thread. You can easily verify that with

System.Diagnostics.Debug.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);

In this case you need to call Dispatcher.Invoke to update UI elements as already @Habib pointed out.

On the other hand you could also use a DispatcherTimer (timer introduced with WPF) instead of System.Timers.Timer. With the DispatcherTimer the tick event will be raised in the main UI thread and you can modify the UI without problems.

Klaus78
  • 11,648
  • 5
  • 32
  • 28