【发布时间】:2020-10-21 13:20:06
【问题描述】:
我需要从同一个进程创建两个(或更多)WPF 窗口。但是窗口必须由单独的线程处理,因为它们不应该能够相互阻塞。我该怎么做?
在 WinForms 中,这是通过以下方式实现的:
- 开始一个新线程
- 从新线程创建表单
- 以表单为参数调用 Application.Run
但是我如何在 WPF 中做同样的事情呢?
【问题讨论】:
标签: wpf multithreading
我需要从同一个进程创建两个(或更多)WPF 窗口。但是窗口必须由单独的线程处理,因为它们不应该能够相互阻塞。我该怎么做?
在 WinForms 中,这是通过以下方式实现的:
但是我如何在 WPF 中做同样的事情呢?
【问题讨论】:
标签: wpf multithreading
正如msdn 所说:
private void NewWindowHandler(object sender, RoutedEventArgs e)
{
Thread newWindowThread = new Thread(new ThreadStart(ThreadStartingPoint));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();
}
private void ThreadStartingPoint()
{
Window1 tempWindow = new Window1();
tempWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
编辑: 这是一个旧答案,但由于它似乎经常被访问,我还可以考虑以下修改/改进(未测试)。
如果您想关闭这样的窗口,只需从线程外部(委托)保留对 Window 对象的引用,然后对其调用 close,如下所示:
void CloseWindowSafe(Window w)
{
if (w.Dispatcher.CheckAccess())
w.Close();
else
w.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(w.Close));
}
// ...
CloseWindowSafe(tempWindow);
如果新线程可以终止(强制中止),符合 cmets 中的问题:
private void ThreadStartingPoint()
{
try{
Window1 tempWindow = new Window1();
tempWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
catch(ThreadAbortException)
{
tempWindow.Close();
System.Windows.Threading.Dispatcher.InvokeShutdown();
}
//the CLR will "rethrow" thread abort exception automatically
}
免责声明:不要在家里这样做,中止线程(几乎总是)违反最佳做法。应该通过各种同步技术中的任何一种来优雅地处理线程,或者在这种情况下,只需通过调用的window.Close()
【讨论】:
ThreadAbortException。最好你删除答案的那部分。 Dispatcher.Invoke + Window.Close 的方法更安全。
不管它值多少钱,因为这个答案是谷歌提供的第一个结果。我还想添加以下来自Eugene Prystupa's Weblog的内容:
“我们的简化解决方案有一个问题。关闭特定窗口并不会终止此窗口的线程调度程序,因此线程继续运行,并且在关闭所有窗口后,进程不会终止并且将成为一个幽灵进程。[简单(但不正确)的解决方案是将我们的线程标记为后台(使用 thread.IsBackground = true;)。这将在主 UI 线程终止时强制它们终止。
正确的实现将在不再需要调度程序时优雅地关闭它。下面的代码是在窗口关闭时终止调度程序的策略示例:"
private void OnCreateNewWindow(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(() =>
{
Window1 w = new Window1();
w.Show();
w.Closed += (sender2, e2) =>
w.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
【讨论】:
正是我想要的。
虽然我是这样做的:
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Thread newWindowThread = new Thread(new ThreadStart(ThreadStartingPoint));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();
var window = new MainWindow(newWindowThread);
window.DataContext = new MainWindowViewModel(window);
window.Show();
}
private void ThreadStartingPoint()
{
SplashWindow tempWindow = new SplashWindow();
tempWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
}
MainWindow.Xaml.cs
public partial class MainWindow : Window
{
public MainWindow(Thread splashWindowThread)
{
InitializeComponent();
MyInializaComponent();
splashWindowThread.Abort();
}
//void DoStuff(){};
}
程序完成所有加载后,我需要我的启动画面消失。
【讨论】:
我想我找到了答案。看看 Jon Skeet 在这个 question 中的回答。
基本上你在你的线程启动方法中这样做:
private void ThreadStartingPoint()
{
Window1 tempWindow = new Window1();
tempWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
【讨论】: