0

It appears that this question has been asked multiple times before, and the suggestions are always the same... to use a dispatcher. I did, and I'm still getting that error.

        var returnedValue = 0;
        HasConnection = false;
        ConnexionProgressBar = true;
        await Task.Run(async () =>
               {
                   returnedValue = await AddSellInvoice();
                   if (returnedValue != 0)
                   {
                       AddSellItems(returnedValue);
                       AddInstallments(returnedValue);
                       Dispatcher.CurrentDispatcher.Invoke(() =>
                       {
                       // Close?.Invoke(this, EventArgs.Empty);
                   });
                   }
               });

Each one of these functions is running SQLLiteCommand. I insert an invoice, get the returned value, then use that value to execute two more different queries. Everything is working fine without any issues, except the commented code above:

Close?.Invoke(this, EventArgs.Empty);

I know that because the code works fine if I don't run it but returns the following error if I do add it back:

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

Now from my understanding and from what I found online, the fix would be to use a Dispatcher, but that's not doing anything. This is the command that opens the dialog, and also what's being triggered whenever Close?.Invoke is called:

await Dispatcher.CurrentDispatcher.Invoke(async () =>
             {

                 var createVenteViewModel = new CreateDialogs.CreateVenteViewModel();
                 var createVenteView = new CreateDialogs.CreateVente(createVenteViewModel);
                 await DialogHost.Show(createVenteView, "MainDialog", (object sender, DialogOpenedEventArgs e) =>
                 {
                     void OnClose(object _, EventArgs args)
                     {

                         createVenteViewModel.Close -= OnClose;
                         e.Session.Close();

                        //  ListeVente.Clear();
                        //  ListeVente = GetListeVente();
                        // _snackbarMessageQueue.Enqueue("La vente a été ajouté avec succée.");

                        //RefreshCommandExecute();
                    }
                     createVenteViewModel.Close += OnClose;
                 });

             });
Siqueler
  • 35
  • 1
  • 5

1 Answers1

1

Quote from Dispatcher.CurrentDispatcher:

Gets the Dispatcher for the thread currently executing and creates a new Dispatcher if one is not already associated with the thread.

That is, you get the Dispatcher of the current thread, which in this case is the thread from the Pool. But you need a dispatcher from the UI element whose Close method you are calling. Assuming that the code you've shown is in the Window Code Behind, it should look like this:

        await Task.Run(async () =>
               {
                   returnedValue = await AddSellInvoice();
                   if (returnedValue != 0)
                   {
                       AddSellItems(returnedValue);
                       AddInstallments(returnedValue);
                       // Dispatcher.CurrentDispatcher.Invoke(() =>
                       Dispatcher.Invoke(() =>
                       {
                            Close?.Invoke(this, EventArgs.Empty);
                       });
                   }
               });

But, if you look more deeply, then I don’t see any point in using the Dispatcher here. You are accessing it on the last line of asynchronous code that is executed with await. Therefore, without losing functionality, you can simply take it out of the achinchronous code:

        await Task.Run(async () =>
               {
                   returnedValue = await AddSellInvoice();
                   if (returnedValue != 0)
                   {
                       AddSellItems(returnedValue);
                       AddInstallments(returnedValue);
                       // Dispatcher.CurrentDispatcher.Invoke(() =>
                       /* Dispatcher.Invoke(() =>
                       {
                            Close?.Invoke(this, EventArgs.Empty);
                       }); */
                   }
               });
        if (returnedValue != 0)
            Сlose?.Invoke(this, EventArgs.Empty);

P.S. If this code is not Code Behind, then you can get the application's main thread dispatcher (this will almost 100% be the element's UI thread) from the property: Application.Current.Dispatcher.

EldHasp
  • 6,079
  • 2
  • 9
  • 24