0
using System;
using System.Linq;
using Microsoft.Practices.Prism.MefExtensions.Modularity;
using Samba.Domain.Models.Customers;
using Samba.Localization.Properties;
using Samba.Persistance.Data;
using Samba.Presentation.Common;
using Samba.Presentation.Common.Services;
using System.Threading;


namespace Samba.Modules.TapiMonitor
{
    [ModuleExport(typeof(TapiMonitor))]
    public class TapiMonitor : ModuleBase
    {

        public TapiMonitor()
        {
            Thread thread = new Thread(() => OnCallerID());
            thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
            thread.Start();
        }

        public void CallerID()
        {
            InteractionService.UserIntraction.DisplayPopup("CID", "CID Test 2", "", "");
        }

        public void OnCallerID()
        {
            this.CallerID();
        }
    }
}

I'm trying to add something to a opensource software package made in C# but I'm having problems with it. The problem with the above (simplified) example is that once InteractionService.UserIntraction.DisplayPopup is called I get an exception "The calling thread cannot access this object because a different thread owns it".

I am not a C# coder but I have tried many things to solve this like Delegates, BackgroundWorkers, etc. etc. and none have worked for me so far.

Can anybody help me with this?

Demigod
  • 11
  • 3
  • What is something. Where is your Interaction.DisplayPopup code? A delegate should be able to solve the issue – Chibueze Opata Feb 22 '13 at 10:20
  • Related: http://stackoverflow.com/questions/11923865/how-to-deal-with-cross-thread-access-exceptions – H.B. Feb 22 '13 at 14:29

2 Answers2

2

Consider calling the method on the UI thread through the Dispatcher. In your case I believe you should pass in the UI dispatcher as a parameter to the constructor of the type you've described and save it in a field. Then, when calling you can do the following:

if(this.Dispatcher.CheckAccess())
{
    InteractionService.UserInteration.DisplayPopup(...);
}
else
{
    this.Dispatcher.Invoke(()=>this.CallerID());
}
Chibueze Opata
  • 9,856
  • 7
  • 42
  • 65
Artak
  • 2,819
  • 20
  • 31
  • I have already tried that but the problem is that the Dispatcher isn't available in the ModuleBase class and I wasn't successful at adding one. :( – Demigod Feb 22 '13 at 10:27
  • There is the ConsoleHoster open-source WPF project, where the Dispatcher is being passed in to the VM. You can take a look to the ViewModelBase class in there to see how its done. But the solution is the same. The link to it is: http://consolehoster.codeplex.com – Artak Feb 22 '13 at 10:49
0

You can write your own DispatcherHelper to acces the Dispatcher from ViewModels. I think it is MVVM friendly. We used an implementation in our app like this:

public class DispatcherHelper
    {
        private static Dispatcher dispatcher;

        public static void BeginInvoke(Action action)
        {
            if (dispatcher != null)
            {
                dispatcher.BeginInvoke(action);
                return;
            }
            throw new InvalidOperationException("Dispatcher must be initialized first");
        }

        //App.xaml.cs
        public static void RegisterDispatcher(Dispatcher dispatcher)
        {
            DispatcherHelper.dispatcher = dispatcher;
        }
    }
Ferenc Kun
  • 428
  • 3
  • 13