NEW TITLE -- The application called an interface that was marshalled for a different thread

2

After making the project simpler, I believe I identified the problem is actually a result the async marshalling.

UPDATE: I made the code simpler to try to figure out what was going on. So here is an update... The Observable collection is being populated on a new thread (async method). I tried moving the assigning of the ItemsSource to after the ObservableCollection is loaded as seen below

   async void LoadAllData(object sender, EventArgs e)
    {

        if (sender != null)
        {
            App.GeoLocationComplete -= LoadAllData;
        }
await ViewModelObjects.NearbyLocations.LoadLocationData();
lvPlaces.ItemsSource = ViewModelObjects.NearbyLocations.GBSLocationDetails;
}

The definition for the data load method is a follows:

public async Task LoadLocationData()
    {....}

When I run this code I get the following error:

The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

I know what is causing the error (the data was loaded on a thread other than the UI thread) but I don't know how to fix it. Suggestions?

UPDATE UPDATE: So I believe I have identified the root cause of the problem but have not figured out how to fix it. I started by simplifying my code as follows and it worked.

    public nearbyplaces()
    {
        InitializeComponent();
        NavigationPage.SetHasNavigationBar(this, false);
        LoadAllData(null, null);
    }

    void LoadAllData(object sender, EventArgs e)
    {

        lobj_Places = new ObservableCollection<GBSLocationDetail>()
        {
            new GBSLocationDetail()
            {
                Title = "Location 1",
                Distance = "20 Miles",
                AddInfo = "Something Else",
                AttributesTexts="Gay, Bar, Dance"
            }
        };

        lvPlaces.ItemsSource = lobj_Places;

    }

HOWEVER, what I need is for the LoadAllData method to be called once I have the GPS location from the device. So in my App.XAML.cs I have the following delegate event declared:

    public static Plugin.Geolocator.Abstractions.IGeolocator gobj_RealGeoCoordinator;
    public static event GeoLocationCompleteEventHandler GeoLocationComplete;
    public static bool gb_WaitingForLocation = true;

Then I have the following code call the event once I get the location back from the device:

   private async void ProcessStartupandResume()
    {

        if (gobj_RealGeoCoordinator == null)
        {
            gobj_RealGeoCoordinator = CrossGeolocator.Current;

            ViewModelObjects.AppSettings.CanAccessLocation = App.gobj_RealGeoCoordinator.IsGeolocationEnabled;

            if (!ViewModelObjects.AppSettings.CanAccessLocation)
            {
                await MainPage.DisplayAlert(ResourceStrings.GetValue("NoLocationServicesTitle"), ResourceStrings.GetValue("NoLocationServicesMessage"), ResourceStrings.GetValue("OKButtonText"));
            }

            //Only add the events if the object has to be created. 
            gobj_RealGeoCoordinator.PositionChanged += gobj_RealGeoCoordinator_PositionChanged;

            gobj_RealGeoCoordinator.PositionError += (sender, e) =>
            {
                ProcessException(new Exception(e.Error.ToString()));
            };
        }

        //Set this to null to trigger the first check
        ib_GPSReenabled = null;

        if (gobj_RealGeoCoordinator.IsListening)
            await gobj_RealGeoCoordinator.StopListeningAsync();

        gobj_RealGeoCoordinator.DesiredAccuracy = 50;

        await gobj_RealGeoCoordinator.StartListeningAsync(10000, 20);

    }

    private static void gobj_RealGeoCoordinator_PositionChanged(object sender, PositionEventArgs e)
    {
        var pos = e.Position;
        ViewModelObjects.AppSettings.Latitude = pos.Latitude;
        ViewModelObjects.AppSettings.Longitude = pos.Longitude;

        if (gb_WaitingForLocation)
        {
            gb_WaitingForLocation = false;
            GeoLocationComplete?.Invoke(new object() , null);
        }
    }

Then in my page I subscribe to the GeoLocationComplete event using the LoadAllData method as seen below. Even when I use a local object and try to set the ItemsSource for the ListView in the code when executed as a result of the event being raised, I receive the error. See code below which subscribed to the event:

    public nearbyplaces()
    {
        InitializeComponent();
        NavigationPage.SetHasNavigationBar(this, false);

        if (App.gb_WaitingForLocation)
            App.GeoLocationComplete += LoadAllData;
        else
            LoadAllData(null, null);
    }

Any suggestions on how to fix this?

c#
listview
data-binding
xamarin.forms
asked on Stack Overflow Nov 3, 2016 by George M Ceaser Jr • edited Nov 3, 2016 by George M Ceaser Jr

1 Answer

1

OK so I figured it out. I needed to invoke the event on the main thread and I did that with the following code:

      Device.BeginInvokeOnMainThread(() =>
            {
                GeoLocationComplete?.Invoke(new object(), null);
            });

After inserting this code, the error was gone. Changing the code back to simply

GeoLocationComplete?.Invoke(new object(), null);

cause the error to occur again. Thus I believe this resolved my problem. Hope this helps someone else. :)

answered on Stack Overflow Nov 4, 2016 by George M Ceaser Jr

User contributions licensed under CC BY-SA 3.0