从新线程创建新表单在另一个表单的Load事件中启动

时间:2012-03-14 04:45:21

标签: c# winforms .net-4.0

快速提问:是否可以从已从其他表单的Load事件中启动的新线程中显示对话框/显示表单?

编辑:为什么无法从另一个表单的Load事件中启动的新线程中显示对话框/显示表单?

假设的要求是显示一个加载器表单/螺旋thingy加载文本,并且可能更多/当MDI子表单加载时挂起整个应用程序。

该要求规定“Loader表单”不得挂起。因此它需要一个新线程。

代码摘要,到目前为止我已经实现了: 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

请不要介意namings以及有2个匹配的开始和停止的事实。

问题是每当我从form1_Load(对象发送者,EventArgs e)中调用Startjob时,在Load方法完成之前不会显示_form(AKA加载器)。

修改 换句话说,在显示实际表单之前,Loader不显示,Loader“显示”(显示在屏幕上)的唯一时间是实际表单完成onLoad方法。 的:EndEdit中

如果我从加载处理程序方法中取出Startjob并在我声明和Show()之前放置它,那么一切都按它应该的方式工作

修改
我假设,在没有任何证据的情况下,在ShowDialog()和Show()方法中调用的Form的CreateHandle()方法访问一个静态属性,检查是否已经创建了任何句柄,如果是,它在当前后面堆叠自己的创建处理创建,因为在这整个过程中,代码尝试关闭()Loader / Splash并抛出一个错误,说“无法关闭()一个创建句柄的表单”。所以我假设CreateHandle()方法有类似于我的

while() { Thread.Yield(); } 

因此创建了2个线程/ 1个是Loader \ Splash创建者线程/它们不断相互收益。

基本上,我不明白为什么所有这一切都在发生,如果我的假设是正确的,为什么表格应该排在一起呢? 的:EndEdit中

我很乐意回答有关代码的所有问题和疑虑,请问我在这里做的事情是否含糊不清,或者您不太了解它的必要性。

1 个答案:

答案 0 :(得分:1)

似乎更合适的解决方案是在单独的线程中执行长时间运行的初始化任务,并在初始化完成后启用MDI子窗口(因此允许MDI子窗口创建完成,但禁用或保留窗口不可见,直到一个单独的加载器线程完成。一旦加载任务完成,启用启动画面或使子窗口可见。

我不确定我是否正确地关注了您当前的代码,但似乎您正在阻止OnLoad事件。这将阻止Windows消息队列,这反过来会阻止应用程序的任何UI呈现。

然后,您可以使用任何WinForms启动屏幕解决方案,直到执行加载的线程完成,例如

http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C