【发布时间】:2012-03-14 04:45:20
【问题描述】:
快速提问:是否可以从另一个表单的 Load 事件中启动的新线程显示对话框/显示表单?
编辑:为什么不能从不同表单的 Load 事件中启动的新线程显示对话框/显示表单?
假定的要求是显示一个加载器表单/螺旋加载文本,可能还有更多/同时加载 MDI 子表单,从而挂起整个应用程序。
要求规定“加载程序表单”不得挂起。因此它需要一个新线程。
到目前为止我已经实现的代码摘要: MDI 家长:
delegate void ManipulateJob();
public void StartJob()
{
Cursor.Current = Cursors.WaitCursor;
if (this.InvokeRequired)
//case when the Loader is started from a different thread than the main we need to invoke
{
System.Diagnostics.Debug.WriteLine("-> MainForm.StartJob InvokeRequired");
ManipulateJob callback = new ManipulateJob(StartJob);
this.Invoke(callback, new object[] { });
}
else
{
tasks_running++;
System.Diagnostics.Debug.WriteLine("-> MainForm.StartJob InvokeNotRequired");
if ((this.t == null || !this.t.IsAlive)&&tasks_running == 1)
{
System.Threading.ThreadStart ts = new System.Threading.ThreadStart(StartDifferent);
this.t = new System.Threading.Thread(ts);
this.t.Name = "UI Thread";
System.Diagnostics.Debug.WriteLine(" **starting thread");
this.t.Start();
while (_form==null||!_form.IsHandleCreated)
//do not continue until the loader form has been shown when this is enabled
//the whole program hangs here when StartJob is called within Load event
{
System.Threading.Thread.Yield();
}
}
}
System.Diagnostics.Debug.WriteLine("<- MainForm.StartJob");
}
private static frmLoading _form;
public void StartDifferent()
{
System.Diagnostics.Debug.WriteLine(" **thread started");
_form = new frmLoading();
System.Diagnostics.Debug.WriteLine(" **loader created");
_form.Icon = this.Icon;
System.Diagnostics.Debug.WriteLine(" **loader icon set");
_form.ShowDialog();
System.Diagnostics.Debug.WriteLine(" **thread terminating");
}
public void StopJob()
{
if (this.InvokeRequired) //in case this is called from a different thread
{
System.Diagnostics.Debug.WriteLine("-> MainForm.StopJob InvokeRequired");
ManipulateJob callback = new ManipulateJob(StopJob);
this.Invoke(callback, new object[] { });
}
else
{
System.Diagnostics.Debug.WriteLine("-> MainForm.StopJob InvokeNotRequired");
if (tasks_running>0&&--tasks_running == 0)
{
StopDifferent();
}
}
System.Diagnostics.Debug.WriteLine("<- MainForm.StopJob");
Cursor.Current = Cursors.Default;
}
delegate void CloseLoadingForm();
public void StopDifferent()
{
System.Diagnostics.Debug.WriteLine("-> MainForm.StopDifferent");
try
{
if (_form != null && _form.IsHandleCreated)
{
CloseLoadingForm callback = new CloseLoadingForm(_form.Close);
//_form itself is always on a different thread thus, invoke will always be required
_form.Invoke(callback);
}
}
finally
{
try
{
if (this.t != null && this.t.ThreadState == System.Threading.ThreadState.Running)
this.t.Join();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(" MainForm.StopDifferent t.Join() Exception: " + ex.Message);
}
}
System.Diagnostics.Debug.WriteLine("<- MainForm.StopDifferent");
}
子表单示例:
private void frmPricingEvaluationConfig_Load(object sender, EventArgs e)
{
//taking out the following and putting it before
//Form frm = new Form();
//frm.Show(); works
if (this.MdiParent is IThreadedParent)
{
((IThreadedParent)this.MdiParent).StopJob();
}
// the loader form is not displayed yet
System.Diagnostics.Debug.WriteLine(" loading pricingEvaluationConfig");
loadPricingClasses();
loadEvaluationClasses();
if (this.MdiParent is IThreadedParent)
{
((IThreadedParent)this.MdiParent).StopJob();
}
}
//loader form displays here, but is unable to close because the closing has been called
请不要介意命名和有 2 组匹配的开始和停止的事实。
问题是每当我从 form1_Load(object sender, EventArgs e) 中调用 Startjob 时,在 Load 方法完成之前不会显示 _form(AKA 加载器)。
编辑: 换句话说,加载器在显示实际表单之前不会显示,加载器“显示”(显示在屏幕上)的唯一时间是实际表单完成时 onLoad 方法。 :ENDEDIT
如果我从负载处理程序方法中取出 Startjob 并将其放在我声明和 Show() 之前,那么一切都会按照预期的方式工作
编辑:
我假设,在没有任何证据的情况下,在 ShowDialog() 和 Show() 方法中调用的 Form 的 CreateHandle() 方法在某处访问静态属性,检查是否已经创建了任何句柄,如果是,它在当前创建之后堆叠自己的创建句柄创建,因为在整个过程中,代码试图关闭()加载器/飞溅并抛出一个错误,说“无法关闭()正在创建句柄的表单”。所以我假设 CreateHandle() 方法有一些类似于我的
while() { Thread.Yield(); }
因此创建了 2 个线程/1 个是 Loader\Splash 创建者线程/,它们不断地相互让步。
基本上,我不明白为什么会发生这一切,如果我的假设是正确的,为什么表单应该一个接一个地排队? :ENDEDIT
我很乐意回答有关代码的所有问题和疑虑,请询问我在这里所做的任何事情是否含糊不清,或者您不太了解它的需要。
【问题讨论】:
-
再深入一点,这个“问题”与 MDI 子父关系有关,因为每当我注释掉声明
//frm.MdiParent = this;时,一切都按照它应该的方式工作