Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different thread owns it in WPF

This is a UserControl that I am using.

this.CardHolderName.Content is a label that is in the UI of the user control.

public partial class PersonCredential : UserControl
        {
            public PersonCredential()
            {
                InitializeComponent();

                Dispatcher.BeginInvoke( (Action) (() => {
                        SCLib type = new SCLib();
                        type.StartMonitoring();

                        type.CardArrived += (string ATR) => { this.CardHolderName.Content = ATR; };
                    };
               }));

I am still getting the error , "The calling thread cannot access this object because a different thread owns it" even though I am using Dispatcher.BeginInvoke.

Is there something wrong in the way the Dispatcher is used ? }

EDIT: I am instantiating that user control inside a content control and the code-behind is:

public partial class MainWindow : Window
    {
       PersonCredential personCredential {get;set;}

        public MainWindow()
        {

            InitializeComponent();
            var personCredential = new CoffeeShop.PersonCredential();
            //create an instance of user control.
            this.personCredentials.Content = personCredential;
            // assign it to the content control inside the wpf main window
            .. // blah blah
        } 

EDIT 1:

Code for start-Monitoring:

public async void StartMonitoring()
        {

            // Wait for user to press a key

            try
            {
                this.establishContext();

                await Task.Run(new Action(WaitForReaderArrival));
                ////WaitForReaderArrival();

                if (IsReaderArrived())
like image 685
now he who must not be named. Avatar asked Feb 10 '14 13:02

now he who must not be named.


2 Answers

EDIT from @DanPuzey's comments. StartMonitoring already monitors on another thread. The key is that the CardArrived event is not being raised from the UI thread:

public PersonCredential()
{
    InitializeComponent();

    SCLib type = new SCLib();
    type.StartMonitoring();

    type.CardArrived += (string ATR) => { 
        // when card arrives, dispatch back to UI thread
        Dispatcher.BeginInvoke(new Action(() => {
            this.CardHolderName.Content = ATR; 
        }));
    };
}

And if you are using .NET 4 or higher, use Task.Factory.StartNew() instead of new Thread().

like image 63
Alden Avatar answered Sep 30 '22 17:09

Alden


If the IsReaderArrived check is an instant non-blocking call (i.e., it takes less than ~50ms to complete), I'd suggest to start the polling loop on the caller's thread, using Task.Delay(interval):

public async Task StartMonitoring(int interval, CancellationToken token)
{
    this.establishContext();

    while (true)
    {
        token.ThrowIfCancellationRequested();

        if (IsReaderArrived())
        {
            // make sure to reset the flag inside IsReaderArrived
            // so the event won't be fired upon the next iteration 

            if (this.CardArrived != null)
                this.CardArrived(this, EventArgs.Empty);
        }

        await Task.Delay(interval);
    }
}

This is an asynchronous loop. If StartMonitoring is called from a UI thread, the CardArrived event will be fired on the same UI thread and the client of your code won't have to worry about Dispatcher.BeginInvoke. If you need an explanation of how this is happening, read "It's All About the SynchronizationContext."

like image 42
noseratio Avatar answered Sep 30 '22 17:09

noseratio