我正在为应用程序开发一个插件,允许创建其他表单。一些插件严格来说是“一劳永逸”的工作者,但有些插件具有进一步控制的UI。该插件公开了一个API(通过.dll),该API将被子类化(通过他们的Core类)。它有一个初始化插件的方法,以及一个关闭插件的方法。
我想要做的是在主线程上有工作线程,在另一个线程上有UI线程,我可以发送事件以及从(开始,停止等)接收控制事件
我当前的实现已成功更新UI;遗憾的是,BeginInvoke是没有用的,因为表单正在我的主线程上初始化,即使它被实例化并显示在子线程中。
为什么在调用form1.UpdateScanningProgress(i)
时会导致GUI线程被阻止?看来this.InvokeRequired在下面的例子中总是假的,即使我在另一个线程中运行该表单。
这是“主要”代码。请注意,插件信息已被删除。我想尽可能基本:
namespace TestBed
{
class TestBedManager : PluginManager
{
Form1 form1;
void UIInitialized(object sender, EventArgs e)
{
}
void BeginScan(object sender, EventArgs e)
{
for (int i = 0; i <= 100; ++i)
{
Thread.Sleep(150);
form1.UpdateScanningProgess(i);
}
Thread.Sleep(1000);
}
// Parent calls when plugin starts
public void PluginStart()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
form1 = new Form1();
form1.Initialized += Initialized;
form1.BeginScan += BeginScan;
Thread thread = new Thread(() =>
{
Application.Run(form1);
});
thread.Start();
// Here is where I'd start my real work instead of simply joining
thread.Join();
}
// Parent calls when plugin needs to stop
public void PluginStop()
{
}
}
}
这是表格代码:
namespace TestBed
{
public partial class Form1 : Form
{
public event EventHandler Initialized;
public event EventHandler BeginScan;
public Form1()
{
InitializeComponent();
}
delegate void UpdateScanningProgressDelegate(int progressPercentage);
public void UpdateScanningProgress(int progressPercentage)
{
if (this.InvokeRequired)
{
UpdateScanningProgressDelegate updateScanningProgress = new UpdateScanningProgressDelegate(UpdateScanningProgress);
updateScanningProgress.BeginInvoke(progressPercentage, null, null);
}
else
{
this.scanProgressBar.Value = progressPercentage;
}
}
delegate void StopScanningDelegate();
public void StopScanning()
{
if (this.InvokeRequired)
{
StopScanningDelegate stopScanning = new StopScanningDelegate(StopScanning);
stopScanning.BeginInvoke(null, null);
}
else
{
this.scanProgressBar.Value = 0;
}
}
private void Form1_Load(object sender, EventArgs e)
{
if (Initialized != null)
Initialized(this, EventArgs.Empty);
}
private void scanButton_Click(object sender, EventArgs e)
{
if (BeginScan != null)
BeginScan(this, EventArgs.Empty);
}
}
}
我觉得我可能错过了一个小细节,并会欣赏一双额外的眼睛。
答案 0 :(得分:0)
我怀疑&#34; this.InvokeRequired&#34;实际上是假的。但无论如何,你是在说错了&#34; BeginInvoke&#34;方法
您应该调用Control.Invoke或Control.BeginInvoke。但是,您正在调用编译器生成的Delegate.BeginInvoke方法。
Control.Invoke / BeginInvoke方法导致在控件的拥有线程上调用给定的委托实例。 Delegate.BeginInvoke方法只是将委托的调用排队到ThreadPool。
现在编写代码的方式,您将进入一个无休止的调用UpdateScanningProgress方法的循环,并且在任何时候都无法实际更新进度UI。可能GUI线程根本没有被阻止......它从来没有被要求做任何有趣的事情。
就个人而言,我不会使用&#34; InvokeRequired&#34;一点都不当它返回false时使用Invoke甚至BeginInvoke是无害的,并且没有它时代码更简单。所以我总是直接调用Invoke / BeginInvoke。
试试这个:
public void UpdateScanningProgress(int progressPercentage)
{
this.BeginInvoke((MethodInvoker)(() => this.scanProgressBar.Value = progressPercentage));
}