C# 如何允许按钮停止操作并使该操作影响窗体控件
因此,基本上,我正在创建音乐软件,希望能够在必要时暂停/停止歌曲。然而,当歌曲播放时,它不允许使用表单上的任何控件,“KeyDown”事件似乎也没有效果,因此我无法停止歌曲,直到它结束 我曾尝试使用“thread”类的一个实例来实现这一点,但问题是我需要在表单上使用某些“Label”控件,这在另一个线程中似乎很难实现 以下是我的“播放”代码的要点C# 如何允许按钮停止操作并使该操作影响窗体控件,c#,multithreading,winforms,C#,Multithreading,Winforms,因此,基本上,我正在创建音乐软件,希望能够在必要时暂停/停止歌曲。然而,当歌曲播放时,它不允许使用表单上的任何控件,“KeyDown”事件似乎也没有效果,因此我无法停止歌曲,直到它结束 我曾尝试使用“thread”类的一个实例来实现这一点,但问题是我需要在表单上使用某些“Label”控件,这在另一个线程中似乎很难实现 以下是我的“播放”代码的要点 Sound[]notes=song.Sounds; 双秒=0; foreach(音符中的声音) { 秒+=(双)音。NoteType.GetDurat
Sound[]notes=song.Sounds;
双秒=0;
foreach(音符中的声音)
{
秒+=(双)音。NoteType.GetDuration(节奏)/1000;
}
timeleft=秒;
lblSecondsLeft.Text=timeleft+“”;
双倍长度=注释长度;
双倍物=(100/长度);
双重其他事物=数学圆(事物);
声音=音符;
双进度=0;
for(int i=0;i
这是一种烦恼,但却是众所周知的烦恼。您必须在单独的(非UI)线程上运行非UI代码,以便UI对按钮、文本更新等保持响应。但是,这意味着您必须在更新期间切换到UI线程,从而从非UI线程更新UI。下面的代码就是这样做的
注1:这是从一些我必须手工编写的代码中删除的,因此没有正确检查
注2:这是WPF版本,WinForms略有不同,但原理相同
public String LabelText{
get {
if (!this.Dispatcher.CheckAccess()) {
return (String)this.Dispatcher.Invoke(new Func<String>(() => { return LabelText; }));
}
return Label.Text;
}
set {
if (!this.Dispatcher.CheckAccess()) {
this.Dispatcher.Invoke(new Action(() => { LabelText = value; }));
return;
}
LabelText = value;
}
}
公共字符串标签文本{
得到{
如果(!this.Dispatcher.CheckAccess()){
返回(字符串)this.Dispatcher.Invoke(newfunc(()=>{return LabelText;}));
}
返回标签.文本;
}
设置{
如果(!this.Dispatcher.CheckAccess()){
Invoke(新操作(()=>{LabelText=value;}));
返回;
}
LabelText=值;
}
}
这是通过检查get/set是否在正确的线程上来实现的,如果不是,则切换线程并调用自身(现在在正确的线程上),完成get或set并返回。如果您不需要线程等待屏幕更新,您也可以使用BeginInvoke,这对于实时音乐来说很可能就是这样。这是一个烦恼,但却是众所周知的烦恼。您必须在单独的(非UI)线程上运行非UI代码,以便UI对按钮、文本更新等保持响应。但是,这意味着您必须在更新期间切换到UI线程,从而从非UI线程更新UI。下面的代码就是这样做的 注1:这是从一些我必须手工编写的代码中删除的,因此没有正确检查 注2:这是WPF版本,WinForms略有不同,但原理相同
public String LabelText{
get {
if (!this.Dispatcher.CheckAccess()) {
return (String)this.Dispatcher.Invoke(new Func<String>(() => { return LabelText; }));
}
return Label.Text;
}
set {
if (!this.Dispatcher.CheckAccess()) {
this.Dispatcher.Invoke(new Action(() => { LabelText = value; }));
return;
}
LabelText = value;
}
}
公共字符串标签文本{
得到{
如果(!this.Dispatcher.CheckAccess()){
返回(字符串)this.Dispatcher.Invoke(newfunc(()=>{return LabelText;}));
}
返回标签.文本;
}
设置{
如果(!this.Dispatcher.CheckAccess()){
Invoke(新操作(()=>{LabelText=value;}));
返回;
}
LabelText=值;
}
}
这是通过检查get/set是否在正确的线程上来实现的,如果不是,则切换线程并调用自身(现在在正确的线程上),完成get或set并返回。如果您不需要线程等待屏幕更新,也可以使用BeginInvoke,这对于实时音乐很可能是如此。好的,您正在UI线程上同步运行所有内容-当然,UI不能使用。
Sound
类是否有异步API(不管是什么)?一个音符一个音符的播放可能不会很好,但是…我想说的是,我以前曾尝试在另一个线程中运行此代码,因此它已经是异步的。嗯,您无法避免使用不同的线程。但是,如果使用Invoke,从另一个线程访问控件是没有问题的。最简单的方法是将表示代码与实际播放分开。然后,您可以根据需要使用例如IProgress
发布任何UI更新。尽管您可能希望改用基于拉的方法,因为您当前的方法要么发布太多更新(当注释较短时),要么发布太少更新(当注释较长时)。计时器读取当前状态并更新UI可能会更好。添加到@Luaan last comment中,您可以将事件NextNote
添加到模型中以更新进度(例如,显示当前播放的音符以及还剩多少音符/秒)和事件已开始
/已完成
(出于明显的原因)(用于播放歌曲)。嗯,您正在UI线程上同步运行所有内容-当然UI不能使用。是否有用于Sound
类的异步API(不管是什么)?一个音符一个音符的播放可能不会很好,但是…我想说的是,我以前曾尝试在另一个线程中运行此代码,因此它已经是异步的。嗯,您无法避免使用不同的线程。但是如果