Home > Enterprise >  WPF error 'calling thread cannot access this object' trying to play sounds in c# class
WPF error 'calling thread cannot access this object' trying to play sounds in c# class

Time:10-01

In my class Clicker.cs, I am attempting to play a sound using a MediaPlayer. However, whenever I call the method to toggle off the MediaPlayer, I get the error that the calling thread cannot access this object because a different thread owns it. This code works fine in MainWindow.xaml.cs, but it does not work in Clicker.cs. I found similar problems online, and the fix was to use Dispatcher.Invoke(). However, this did not work. The error specifically happens when StopSoundClicker() is called. My code is below:

private static MediaPlayer player = new MediaPlayer();
private static bool isPlaying = false;

public Clicker(KeyInfo k)
{
    player.Open(new Uri(@"C:\Users\chris\Downloads\Recording2.wav"));
    player.MediaEnded  = Loop;
}

private void StartSoundClicker()
{
    player.Play();
    isPlaying = true;
}

private static void Loop(object? sender, EventArgs? e)
{
    Application.Current.MainWindow.Dispatcher.Invoke(() =>
    {
        if (isPlaying)
        player.Play();
    });
}

private async void StopSoundClicker()
{
    await Application.Current.MainWindow.Dispatcher.Invoke(async () =>
    {
        await Task.Delay(70);
        player.Stop();
        isPlaying = false;
    });
}

CodePudding user response:

The error occurs when you're calling .Dispatcher on MainWindow instnace from background thread.

Replace

Application.Current.MainWindow.Dispatcher

With

Application.Current.Dispatcher

I think this variant is what StopSoundClicker should be!

private async Task StopSoundClicker()
{
    await Task.Delay(70);
    Application.Current.Dispatcher.Invoke(() =>
    {
        player.Stop();
        isPlaying = false;
    });
}

CodePudding user response:

In general, you should use the Dispatcher of the element you are accessing.
Of course, this will almost always be Application.Current.Dispatcher.
But in some cases this may not be the case.
And in CodeBehind, you can simply use Dispatcher - this is a property of the Window. And all its elements have the same Dispatcher.

But in your task, I don't see the need to use it at all - Clickers are always called on the UI thread.

In my opinion, this code should work:

private async void StopSoundClickerAsync(object sender, RoutedEventArgs e)
{
   await Task.Delay(70);

   player.Stop();
   isPlaying = false;
}
  • Related