简体   繁体   中英

Passing UI Thread method to another thread for calling in C#

I have a Windows Forms project in C#. In this project there is a WaveOut device that's played back in separate thread. This playback thread periodically needs to call a UI thread method and pass it some data (an array holding the audio information that is being passed to the soundcard). The passAudio method calls the connected EventHandler periodically.

Right now, the waveout device (WaveOutPlayer.cs) has an EventHandler:

public class WaveOutPlayer : IDisposable
{
    public event EventHandler<AudioEventArgs> BufferSwapped;
    ...
    private void passAudio(byte[] pAudiodata)
    {
        AudioEventArgs args = new AudioEventArgs();
        args.Data = pAudiodata;
        args.WaveFormat = ws.Format;
        if (BufferSwapped != null)
        {
            BufferSwapped.Invoke(this, args);
        }
    }
}

And the Windows Form instance connects to this EventHandler:

private void Start()
{
    WaveStream Audio = new WaveStream("sine440hz_16bit_stereo.wav");
    WaveOutPlayer wp = new WaveOutPlayer(audio, 0);
    wp.BufferSize = 8192; // testing
    wp.Repeat = false; // 'true' not implemented yet
    wp.BufferSwapped += Wp_BufferSwapped;
}

private void Wp_BufferSwapped(object sender, AudioEventArgs e)
{
    // The audio buffer data can be found in the event args.
    // So analyze this Audio and manipulate some of the forms' controls 
    // accordingly.

    this.labelForAmplitude.Text = "some value";
}

However, this results in an exception because the Wp_BufferSwapped-Method actually belongs to the playback thread and thus may not manipulate the label's text.

Now: How can I solve this problem without making the Windows Form's code more difficult? The reason for this is that I want to let my students (high school) do some cool stuff with arrays and simple user interfaces. But at this point they only have a very basic understanding of user interfaces work. They do not know anything about stuff like BeginInvoke or MethodInvoker, yet. So I want to give them the WaveOutPlayer in form of a DLL - and they only need to deal with the Windows Form. Is there a solution for this particular Kind of problem?

You can capture current SynchronizationContext in constructor and then Post your event handler invocation to it, like this:

public class WaveOutPlayer {
    private readonly SynchronizationContext _context;
    public WaveOutPlayer() {
        // capture
        _context = SynchronizationContext.Current;
    }

    public event EventHandler<AudioEventArgs> BufferSwapped;

    private void passAudio(byte[] pAudioData) {
        var args = new AudioEventArgs();
        args.Data = pAudioData;
        var handler = BufferSwapped;
        if (handler != null) {
            if (_context != null)
                // post
                _context.Post(_ => handler(this, args), null);
            else
                handler(this, args);
        }
    }
}

By doing this you do not introduce dependency on winforms to your WaveOutPlayer while at the same time no compilcated actions are required from WinForms part, event handlers are just invoked on UI thread. Note that Post here will be analog to Control.BeginInvoke . If you want analog of Control.Invoke - use Send instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM