我的应用程序有一些异步运行的网络代码。当没有连接到服务器时,我附加了一些要抛出的事件,当发生这种情况时,我正在创建一些“操作失败”的表单。问题是我的表单在创建后挂起。我读到了这个,我尝试用:
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);
}
}
}
你能告诉我这个具体问题要改变什么吗?
答案 0 :(得分:4)
作为第一步,使用:
从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();
}
}
答案 1 :(得分:2)
将在Show-call期间创建窗口句柄。所以在主UI线程中显示表单总是好的!只需切换到该线程,然后调用Show()。
答案 2 :(得分:1)
您对Invoke
和InvokeRequired
的理解有点偏差; InvokeRequired
将随时返回true
正在从其创建的线程以外的线程(通常称为“UI线程”)访问控件。
因此,如果您尝试从其他主题调用Show()
或Hide()
,则确实需要Invoke
。
除了简短的解释外,您还没有提供足够的信息来真正提供任何其他想法。也许你可以发布一些相关的代码,比如在加载或激活表单时执行的任何代码。
修改强>
在创建和显示新表单之前,您需要返回UI线程。正如在评论中指出的那样,将它显示为您的应用程序启动然后隐藏它的工作原理,因为这一切都发生在UI线程上。
您可以这样做的一种方法是,如果您有一个始终可见的“MainForm”,您可以将ShowView
方法移动到该表单,并使用InvokeRequired
`Invoke`模式保持UI线程的工作。
另一个选项是默认情况下将WindowState
设置为Minimized
,以便在最初显示时(在应用程序启动时)它在屏幕上不可见(您也可以设置{{1}到假)。然后,您的ShowInTaskbar
方法也可以更改ShowView
。