C# 将UI线程方法传递给另一个线程以在C中调用#

C# 将UI线程方法传递给另一个线程以在C中调用#,c#,multithreading,winforms,invoke,C#,Multithreading,Winforms,Invoke,我有一个用C#编写的Windows窗体项目。在这个项目中,有一个WaveOut设备,可以在单独的线程中播放。此播放线程定期需要调用UI线程方法并向其传递一些数据(保存正在传递到声卡的音频信息的数组)。 passAudio方法定期调用连接的EventHandler 现在,WaveOutPlayer设备(WaveOutPlayer.cs)有一个EventHandler: public class WaveOutPlayer : IDisposable { public event Event

我有一个用C#编写的Windows窗体项目。在这个项目中,有一个WaveOut设备,可以在单独的线程中播放。此播放线程定期需要调用UI线程方法并向其传递一些数据(保存正在传递到声卡的音频信息的数组)。 passAudio方法定期调用连接的EventHandler

现在,WaveOutPlayer设备(WaveOutPlayer.cs)有一个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);
        }
    }
}
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";
}
但是,这会导致异常,因为Wp_BufferSwapped-Method实际上属于回放线程,因此可能无法操作标签的文本

现在: 如何在不增加Windows窗体代码难度的情况下解决此问题?原因是我想让我的学生(高中生)用数组和简单的用户界面做一些很酷的事情。但在这一点上,他们对用户界面的工作只有非常基本的了解。他们对BeginInvoke或MethodInvoker之类的东西一无所知。 所以我想给他们一个DLL形式的WaveOutPlayer,他们只需要处理Windows形式。
这类问题有解决办法吗

您可以在构造函数中捕获当前的
SynchronizationContext
,然后
Post
您对它的事件处理程序调用,如下所示:

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);
        }
    }
}
公共类WaveOutPlayer{
私有只读同步上下文\u上下文;
公共WaveOutPlayer(){
//俘获
_context=SynchronizationContext.Current;
}
已交换公共事件事件处理程序缓冲区;
专用void passAudio(字节[]数据){
var args=新的AudioEventArgs();
args.Data=pAudioData;
var handler=BufferSwapped;
if(处理程序!=null){
如果(_context!=null)
//职位
_Post(=>handler(this,args),null);
其他的
处理程序(this,args);
}
}
}

通过这样做,您不会将对winforms的依赖引入到
WaveOutPlayer
,同时winforms部分不需要编译操作,事件处理程序只在UI线程上调用。请注意,此处的
Post
Control.BeginInvoke
类似。如果您想要模拟
控件。调用
-使用
发送

您可以将
此.labelForAmplitude.Text
作为
ref
发送到
Wp\u BufferSwapped-Method
可能重复的@Usmanlqbal,如果只有一个或两个标签需要更改,这将是一个解决方案。但是,我想让学生决定更改哪些标签或图片框。可以想象覆盖事件的
add
remove
处理程序,在添加过程中捕获
SynchronizationContext
,并将每个代理和上下文保持在一起,以便在分派事件时使用。然而,我目前只是在弥补,因此无法提供健壮的实现。当然,还有一个风险,即学生可能会错误地认识到,从事件处理程序更新UI是安全的,然后您将不得不以某种方式撤消它。有时太多的牵制可能会适得其反。在WaveOutPlayer的构造函数中捕获当前SynchronizationContext并在该上下文上调用事件处理程序。工作非常完美!非常感谢你!!