有人可以解决我遇到的问题吗?
我正在开发一个wpf项目。方案如下:
我需要在主UI线程上弹出一个窗口(模型窗口),然后关闭它。这些工作是从另一个UI线程开始的(阻止用户点击主UI窗口。)然后我关闭这个窗口。主要代码如下所示。它有效。
据我所知,在ShowDialog()
返回之前不会出现close方法(至少在UI线程上是这种情况,我的意思是没有调度程序的代码),有没有人有多线程的经验?
Window window;
private void Button_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(() =>
{
//create a window and let user work from this thread
//code is omitted.
//create another window on main UI thread
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
window = new Window();
window.ShowDialog();
}));
//do some work here
Thread.Sleep(1000);
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
//Thread.Sleep(1000);
window.Close();
}));
});
thread.Start();
}
感谢您的时间!
答案 0 :(得分:19)
因此,如果我正确理解您的问题,您就会说这段代码完全符合您的要求,但您只是想了解如何(和为什么)它有效吗?
这是它的工作原理。首先,您的线程运行此代码:
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
window = new Window();
window.ShowDialog();
}));
将您的操作排在主(UI)线程的调度程序队列中,然后立即返回:您的工作线程继续运行。
当应用程序首次启动时(通常通过编译器生成的代码初始化App.xaml对象,虽然您也可以通过调用Application.Run显式执行),但它启动了它的消息循环,这就是这样的(伪代码,非常简化):
public class Application {
public void Run() {
while (!Exited && action = Dispatcher.DequeueAction())
action();
}
}
因此,在排队操作后的某个时刻,UI线程将绕过将您的操作从队列中拉出并运行它,此时您的操作会创建一个窗口并以模态方式显示它。
模态窗口现在开始自己的消息循环,这就像这样(再次,非常简化):
public class Window {
public bool? ShowDialog() {
DisableOtherWindowsAndShow();
while (!IsClosed && action = Dispatcher.DequeueAction())
action();
EnableOtherWindowsAndHide();
return DialogResult;
}
}
稍后,您的工作线程运行此代码:
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
window.Close();
}));
同样,您的操作排队到UI线程的调度程序队列,然后BeginInvoke调用立即返回,您的工作线程继续运行。
迟早,UI线程的消息循环将绕过队列并执行您的操作,告诉窗口关闭。这与用户单击标题栏的“X”按钮具有基本相同的效果,当然即使您在模态对话框中也可以这样做。这会导致ShowDialog的消息循环终止(因为窗口现在已关闭),此时对话框被隐藏,其他窗口重新启用,ShowDialog返回,原始(ShowDialog)操作完成,因此返回,控制落下返回Application.Run中的原始消息循环。
请注意,每个线程有一个调度程序队列,不是每个消息循环一个。因此,您的“关闭”操作会进入与“显示对话框”操作相同的队列。这是一个不同的代码片段,现在进行消息循环轮询(ShowDialog中的一个代码而不是Application.Run中的一个),但循环的基础是相同的。
答案 1 :(得分:3)
BeginInvoke
是一种非阻塞方法;它将操作添加到调度程序队列,而不是等待其完成。您应该使用Invoke
代替,它在调度程序线程上同步调用该方法。