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

本文关键字:show 挂起 调用 线程 另一个 窗体 | 更新日期: 2023-09-27 18:10:53

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

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

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

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

这样似乎是有效的。但它是相当丑陋的,当我的应用程序启动时,我可以看到一会儿我的形式被显示,然后消失。我应该如何使我的窗体创建它的所有控件而不显示它,或者有一些更好的解决方案来解决这个问题?

EDIT:更多信息。我使用MVP设计模式。我有依赖于IView的Presenter。我的表单实现了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);
            }
        }
    }

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

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

第一步使用:

删除ShowForm2()中的递归
Action a = new Action(() => form.Show());

现在详细解释一下发生了什么:当这些行在button1_Click()

中注释时
    //form.Show();
    //form.Hide();

then以ShowForm2()的形式。invokerequrequired将为false。这意味着表单在同一个线程中执行的工作,这就是为什么表单"挂起"。

但是当您取消注释这些行时,则相同的形式。invokerequrequired将为true,这意味着form在UI线程中执行,这就是为什么form2是响应式的。

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

解决方法是使用表单。创建窗体后的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将返回true 任何时候控件正在从线程访问,而不是创建它的线程(通常称为"UI线程")。

所以如果你试图从另一个线程调用Show()Hide(),你确实需要Invoke它。

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

编辑

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

你可以做到这一点的一种方法是,如果你有一个"MainForm"总是可见的,你可以移动你的ShowView方法到那个表单,并使用InvokeRequired ' Invoke '模式来保持工作在UI线程上。

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