繁体   English   中英

从另一个线程调用show后,Windows窗体窗体挂起

[英]Windows Forms Form hanging after calling show from another thread

我有一些应用程序,它有一些异步运行的网络代码。 当没有连接到服务器时,我附加了一些要抛出的事件,当发生这种情况时,我正在创建一些“操作失败”的表单。 问题是我的表单在创建后挂起。 我读到了这一点,我尝试用:

public void ShowView()
{
    if (this.InvokeRequired)
    {
        Action a = new Action(ShowView);
        this.Invoke(a);
    }
    else this.Show();
}

问题仍然存在。 比我发现如果没有创建控件,InvokeRequired返回false。 所以我在初始化代码中添加了:

this.show();
this.hide();

这种方式似乎有效。 但它非常难看,当我的应用程序启动时,我可以看到我的表单被显示而不是消失。 我应该如何让我的表单创建所有控件而不显示它,或者是否有更好的解决方案来解决这个问题?

编辑:更多信息。 我正在使用MVP设计模式。 我有Presenter,它依赖于IView。 我的表单实现了IView。 IView有我从我的演示者调用的ShowView()和HideVIew()方法。 我的演示者从另一个线程接收事件。 那么我应该在哪里进行这种线程跳转或者我应该如何解决这个问题?

EDIT2:这里示例应用程序说明问题:

public partial class Form1 : Form
    {
        Form2 form;

        public Form1()
        {
            InitializeComponent();
            form = new Form2();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //form.Show();
            //form.Hide();
            Thread t = new Thread(new ThreadStart(ShowForm2));
            t.Start();
        }

        private void ShowForm2()
        {
            if (form.InvokeRequired)
            {
                Action a = new Action(ShowForm2);
                form.Invoke(a);
            }
            else
            {
                form.Show();
                Thread.Sleep(5000);
            }
        }
    }

你能告诉我这个具体问题要改变什么吗?

作为第一步,使用以下命令从ShowForm2()中删除递归:

Action a = new Action(() => form.Show());

现在详细说明会发生什么:当这些行在button1_Click()中被注释时

    //form.Show();
    //form.Hide();

然后在ShowForm2()form.InvokeRequired将为false。 这意味着表单与您的工作在同一个线程中执行,这就是为什么表单“挂起”。

但是当您取消注释这些行时,则相同的form.InvokeRequired将为true,这意味着表单在UI线程中执行,这就是form2响应的原因。

解决方案是强制form2在UI线程中运行,但是你不希望像你的例子那样闪烁,所以你必须尝试使用​​其他方法。

解决方法是在创建表单后使用form.Handle属性。 Form.Handle在首次使用时创建。 在你的情况下是在form.Show()。 显然,在所需的线程中创建Handle非常重要,而不仅仅是表单包装器。 我将附加修改后的代码以使事情更清楚。

我不确定解释是否正确,但是handle = form.Handle; 将解决您的问题。

public partial class Form1 : Form
{
    Form form;
    IntPtr handle;

    public Form1()
    {
        InitializeComponent();
        form = new Form();
        handle = form.Handle;
    }

    private void ShowForm2()
    {
        if (form.InvokeRequired)
        {
            Action a = new Action(() => form.Show());
            form.Invoke(a);
        }
        else
        {
            form.Show();
            Thread.Sleep(5000);
        }
    }

    private void button1_Click_1(object sender, EventArgs e)
    {
        //form.Show();
        //form.Hide();
        Thread t = new Thread(new ThreadStart(ShowForm2));
        t.Start();
    }
    }

窗口句柄将在Show-call期间创建。 所以在主UI线程中显示表单总是好的! 只需切换到该线程,然后调用Show()。

您对InvokeInvokeRequired理解有点InvokeRequired ; InvokeRequired将返回true 随时随地的控制正在从高于它创建的线程以外的线程访问(通常被称为“UI线程”)。

因此,如果您尝试从另一个线程调用Show()Hide() ,则确实需要Invoke它。

除了简短的解释,您还没有提供足够的信息来真正提供任何其他想法。 也许你可以发布一些相关的代码,比如在加载或激活表单时执行的任何代码。

编辑

在创建和显示新表单之前,您需要返回UI线程。 正如在评论中指出的那样,将它显示为您的应用程序启动然后隐藏它的工作原理,因为这一切都发生在UI线程上。

你可以做到这一点的方法之一是,如果你有一个“MainForm的”始终可见,你可以移动ShowView方法这种形式,并使用InvokeRequired `Invoke`模式来保持对UI线程的工作。

另一个选项是默认情况下将WindowState设置为Minimized ,这样当它最初显示时(在应用程序启动时)它在屏幕上不可见(您也可以将ShowInTaskbar设置为false)。 然后您的ShowView方法也可以更改WindowState

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM