【问题标题】:Windows Forms Form hanging after calling show from another thread从另一个线程调用 show 后 Windows 窗体窗体挂起
【发布时间】:2011-10-11 14:43:46
【问题描述】:

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

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

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

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

这样它似乎工作了。但它非常难看,当我的应用程序启动时,我可以看到我的表单显示了一会儿然后消失了。 我应该如何让我的表单在不显示的情况下创建所有控件,或者有更好的解决方案吗?

编辑:更多信息。我正在使用 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);
            }
        }
    }

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

【问题讨论】:

  • 您需要一个已创建的 Form 实例。应该不难,如果你真的需要,请使用 Application.OpenForms[0]。
  • ShowView 是我的表单中的方法,它继承了 Form 类,所以我调用 show 方法来显示我的表单。我应该调用另一种方法来显示表单吗?

标签: c# winforms multithreading


【解决方案1】:

作为第一步,使用以下方法从 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 = 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();
    }
    }

【讨论】:

  • 感谢您的回答,它救了我! handle = form.Handle;form.WindowsState = FormWindowState.Minimized; form.ShowInTaskbar = false; form.Show(); form.Hide(); form.WindowsState = FormWindowState.Normal; form.ShowInTaskbar = true; 优雅得多
【解决方案2】:

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

【讨论】:

  • @Hooch - 我认为 Fischermaen 不明白他在说什么。
  • 大声笑,我写了新的答案并用这个例子玩了半个小时,只是为了了解 Fischermaen 给了我们关于问题的好点,所以投票吧,看起来 Ramhound 不明白他在说什么关于 ;) 我忽略了这个答案,因为没有投票和糟糕的 cmets ;(
【解决方案3】:

您对InvokeInvokeRequired 的理解有些偏离; InvokeRequired 将返回true 任何时候一个控件正在被创建它的线程以外的线程访问(通常称为“UI 线程”)。

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

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

编辑

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

您可以这样做的一种方法是,如果您有一个始终可见的“MainForm”,您可以将您的ShowView 方法移动到该表单,并使用InvokeRequired`Invoke` 模式来继续工作UI 线程。

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

【讨论】:

  • 是的,只要从创建控件的线程以外的线程访问控件,它就会返回 true。如果控制,但如果它没有被创建,那将返回 false。如果我从 UI 线程调用第一个 show() hide(),为什么我的应用程序可以正常工作,而当我不这样做时它不起作用?
  • @Vajda - 表单第一次打开但隐藏后。所以显示表单的调用只是取消隐藏表单。
  • 是的,我明白了。但是如何避免这个 show() hide() 调用呢?
  • @Vajda 你为什么要这样做?
  • @Coding Gorilla 因为它看起来像是一些解决方法,而不是真正的解决方案。请查看我编辑的帖子以获取问题示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多