情况如下: 我正在开发一个具有以下结构的简单应用程序:
右?
好吧,在 FormMain 中,我有以下功能:
private void DoItInNewThread(ParameterizedThreadStart pParameterizedThreadStart, object pParameters, ThreadPriority pThreadPriority)
{
Thread oThread = new Thread(pParameterizedThreadStart);
oThread.CurrentUICulture = Settings.Instance.Language;
oThread.IsBackground = true;
oThread.Priority = pThreadPriority;
oThread.Name = "μRemote: Background operation";
oThread.Start(pParameters);
}
因此,每次我需要调用位于 ComplexFunctions 上的耗时方法时,我会执行以下操作:
// This is FormMain.cs
string strSomeParameter = "lala";
DoItInNewThread(new ParameterizedThreadStart(ComplexFunctions.DoSomething), strSomeParameter, ThreadPriority.Normal);
另一个类FormNotification,它是一个向用户显示进程信息的表单。 可以从FormMain或ComplexFunctions调用此FormNotification。 例如:
// This is ComplexFunctions.cs
public void DoSomething(string pSomeParameter)
{
// Imagine some time consuming task
FormNotification formNotif = new FormNotification();
formNotif.Notify();
}
FormNotify有一个计时器,因此,在10秒后关闭表单。我没有使用formNotif.ShowDialog,因为我不想把焦点放在这个Form上。 您可以查看this link以查看我在通知中所做的工作。
好的,这是问题所在: 当我从 ComplexFunction 中调用 FormNotify 时,从 FormMain 中的另一个线程调用...此 FormNotify 在消失之后消失几毫秒。 这与你做这样的事情的效果相同:
using(FormSomething formSomething = new FormSomething)
{
formSomething.Show();
}
如何避免这种情况?
这些是我不想使用的可能解决方案:
这是一个简化的场景(FormNotify会做一些其他花哨的东西,只停留10秒钟,但它们与看到问题无关)。
谢谢你的时间! 拜托,对不起我的英语。
答案 0 :(得分:5)
您不能从其他线程进行WinForms调用。查看表单中的BeginInvoke - 您可以调用委托来显示UI线程中的表单。
编辑:从评论(不要将CheckForIllegalCrossThreadCalls设置为false)。
更多信息 几乎每个GUI库都被设计为仅允许在为此目的指定的单个线程(称为UI线程)中进行更改GUI的调用。如果您在另一个线程中,则需要安排调用以更改在UI线程中创建的GUI。在.NET中,这样做的方法是调用Invoke(同步)或BeginInvoke(异步)。等效的Java Swing调用是invokeLater() - 几乎每个GUI库都有类似的函数。
答案 1 :(得分:5)
几乎每个GUI库都只允许在为此目的指定的单个线程中调用更改GUI的调用(称为UI线程)。如果您在另一个线程中,则需要安排调用以更改在UI线程中创建的GUI。在.NET中,这样做的方法是调用Invoke(同步)或BeginInvoke(异步)。等效的Java Swing调用是invokeLater() - 几乎每个GUI库都有类似的函数。
有一种称为线程亲和力的东西。 WinForm应用程序中有两个线程,一个用于呈现,另一个用于管理用户界面。您只处理用户界面线程。渲染线程保持隐藏状态 - 在后台运行。在UI线程上创建的唯一对象可以操纵UI - 即对象与UI线程具有线程关联。
因为,您尝试从与UI线程不同的线程更新UI(显示通知)。所以在你的工作线程中定义一个委托并让FormMain听这个事件。在事件处理程序中(在FormMain中定义)编写代码以显示FormNotify。
当您要显示通知时,从工作线程触发事件。
当控件的创建线程以外的线程尝试访问该控件的某个方法或属性时,通常会导致不可预测的结果。常见的无效线程活动是在错误的线程上调用访问控件的Handle属性。将CheckForIllegalCrossThreadCalls设置为true可在调试时更轻松地查找和诊断此线程活动。请注意,在调试器外部启动应用程序时,非法的跨线程调用将始终引发异常。
注意:设置CheckForIllegalCrossThreadCalls只能在DEBUGGIN SITUATIONS中完成。不可预知的结果将会发生,你最终会试图追逐你会发现难以找到的错误。
答案 2 :(得分:1)
有一种称为线程亲和力的东西。 WinForm应用程序中有两个线程,一个用于呈现,另一个用于管理用户界面。您只处理用户界面线程。渲染线程保持隐藏状态 - 在后台运行。在UI线程上创建的唯一对象可以操纵UI - 即对象与UI线程具有线程关联。
因为,您尝试从与UI线程不同的线程更新UI(显示通知)。所以在你的工作线程中定义一个委托并让FormMain听这个事件。在事件处理程序中(在FormMain中定义)编写代码以显示FormNotify。
当您要显示通知时,从工作线程触发事件。
答案 3 :(得分:0)
使用 SetWindowPos API调用确保您的通知表单是最顶层的窗口。这篇文章解释了如何:
http://www.pinvoke.net/default.aspx/user32/SetWindowPos.html
答案 4 :(得分:0)
假设您在表单中有按钮,并希望在用户点击该按钮时打开另一个表单Form1
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(this.ShowForm1));
t.Start();
}
您需要做的就是检查InvokeRequired
属性,如果是,请调用表单的Invoke方法,通过ShowForm1
委托,这将最终进行递归调用InvokeRequired
将为false < / p>
delegate void Func();
private void ShowForm1()
{
if (this.InvokeRequired)
{
Func f = new Func(ShowForm1);
this.Invoke(f);
}
else
{
Form1 form1 = new Form1();
form1.Show();
}
}