Прохождение потока пользовательского интерфейса способ получить другой поток для вызова в C#
У меня есть проект Windows Forms на C#. В этом проекте есть устройство WaveOut, которое воспроизводится в отдельном потоке. Этот поток воспроизведения периодически должен вызывать метод потока пользовательского интерфейса и передавать ему некоторые данные (массив, содержащий аудиоинформацию, передаваемую на звуковую карту). Метод passAudio периодически вызывает подключенный обработчик событий.
Прямо сейчас, устройство waveout (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);
}
}
}
И экземпляр формы Windows подключается к этому 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";
}
Однако это приводит к исключению, так как Wp_BufferSwapped-метод фактически принадлежит потоку воспроизведения и, следовательно, не может манипулировать текстом метки.
Теперь: Как решить эту проблему, не усложняя код формы Windows? Причина этого в том, что я хочу, чтобы мои ученики (старшеклассники) делали некоторые интересные вещи с массивами и простыми пользовательскими интерфейсами. Но на данный момент у них есть только очень базовое понимание пользовательские интерфейсы работают. Они еще ничего не знают о таких вещах, как BeginInvoke или MethodInvoker. Поэтому я хочу дать им WaveOutPlayer в виде DLL-и им нужно только иметь дело с формой Windows. Есть ли решение для этой конкретной проблемы?
1 ответ:
Вы можете захватить текущий
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); } } }
Делая это, вы не вводите зависимость от winforms в свой
WaveOutPlayer
, в то же время от WinForms не требуется никаких компилированных действий, обработчики событий просто вызываются в потоке пользовательского интерфейса. Заметим, чтоPost
здесь будет аналогомControl.BeginInvoke
. Если вам нужен аналогControl.Invoke
- Используйте вместо негоSend
.