我有一种导出数据的方法。我通过一个新线程来做这件事,以便GUI保持响应。最后它打开一个SaveFileDialog
,如果没有调用就无法工作。通过以下修改,它再次起作用,GUI没有响应。任何线索?
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(method);
thread.Start();
}
public void medhod()
{
if (this.InvokeRequired)
{
Invoke(new MethodInvoker(delegate() { method(); }));
}
else
{
//Code
//SaveFileDialog
}
}
*编辑:另一种方法是将导出代码留在新线程中,并将SaveFileDialog
放回原始线程。我需要的只是第一个线程“暂停”,然后在第二个线程结束后继续。欢迎提出意见。
答案 0 :(得分:3)
问题是在非UI线程中运行任何类型的UI组件通常都是坏的想法 - 尤其是模态对话框。
相反,将实际的后台处理代码放入另一个线程中,一旦完成回调到UI线程并启动保存对话框。 TPL使得这类事情变得非常微不足道,例如。
Task.Factory.StartNew(() => {
// do background processing
}).ContinueWith((task) => {
// show save dialog
}, TaskScheduler.FromCurrentSynchronizationContext());
答案 1 :(得分:3)
你的问题可能就是Luaan评论所指出的。你有很长的操作要放入线程,但是然后你将整个操作调用到UI线程中,它会在一段时间内阻止UI线程。
这样做:
private void button1_Click(object sender, EventArgs e)
{
(new Thread(method)).Start();
}
private void method()
{
//Code
Invoke(() =>
{
//SaveFileDialog
});
}
您无需检查InvokeRequired
,因为无论如何都需要它。你使用它的方式是一种定义方法的模式,可以从任一线程调用。但在这种情况下,它通常包含与UI控件交互的非常短的操作。
答案 2 :(得分:-1)
尝试使用异步的BeginInvoke()而不是同步的Invoke 请参阅What's the difference between Invoke() and BeginInvoke()以更好地了解该论点。
答案 3 :(得分:-1)
试试这个:
void btnClick(object sender, EventArgs e)
{
var t = new Thread(doStuff);
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
void doStuff()
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.ShowDialog();
}
SaveFileDialog在一个单独的线程中运行(它必须有一个单线程的公寓),并且它不会阻止应用程序的UI线程。但是,要非常小心你所做的一切,它可能会非常不稳定。
基本问题是您的应用程序正在运行Windows Messaging循环,它基本上是一个循环,它遍历来自OS(和其他应用程序)的Windows消息。如果循环由于某种原因而卡住,则应用程序变得无响应(当您单击鼠标时,操作系统会向您的消息队列发送WM_MOUSEDOWN和更多方法,这些方法必须由消息循环减少以执行任何操作)。使用ShowDialog
方法正是循环卡住的方式之一 - 您的表单无法再处理任何消息,因为它永远不会有机会。
现在,Invoke
的作用是,它将您要调用的方法添加到目标表单上的另一个队列。表单在Windows消息传递循环中获得的下一次机会,它执行调用队列中的所有项目 - 再次,消息传递循环被卡住。
现在,对话框如何接收Windows消息?在实践中,它很容易创建自己的WM循环。这是一个非常小的但是另一个while
循环,它只在模式对话框关闭时终止 - 阻止父表单(或者说应用程序)的消息循环。
上面代码的问题是它可能会从我们的父窗口窃取消息循环。解决方案是显式创建一个带有新消息传递循环的新窗口,方法是将所有者显式传递给ShowDialog
:
void doStuff()
{
NativeWindow nw = null;
try
{
nw = new NativeWindow();
nw.CreateHandle(new CreateParams());
SaveFileDialog sfd = new SaveFileDialog();
sfd.ShowDialog(nw);
}
finally
{
if (nw != null)
nw.DestroyHandle();
}
}