WPF 如何在线程中打开子窗体

时间:2022-08-27 23:44:13

    最近遇到一个问题,在wpf程序的某个线程中打开子窗体时显示“调用线程必须为 STA,因为许多 UI 组件都需要”,这是典型的子线程更新UI异常问题了,解决方法是用Dispatcher的invoke方法来执行UI操作。

    Dispatcher的字面意思是“调度员”,很形象地解释了它的作用——处理并发和多线程。Dispatcher本身是一个单例模式,构造函数私有,暴露了一个静态的CurrentDispatcher方法用于获得当前线程的Dispatcher,Dispatcher内部维护了一个静态的 List<Dispatcher> _dispatchers, 每当使用CurrentDispatcher方法时,它会在这个_dispatchers中遍历,如果没有找到,则创建一个新的Dispatcher对 象,加入到_dispatchers中去。Dispatcher内部维护了一个Thread的属性,创建Dispatcher时会把当前线程赋值给这个 Thread的属性,下次遍历查找的时候就使用这个字段来匹配是否在_dispatchers中已经保存了当前线程的Dispatcher。WPF的控件均继承自DispatcherObject,具有线程关联特征,包含了 Dispatcher 的线程(通常指默认 UI 线程)才能对UI控件进行操作。

   回到问题,因为工作线程并不能操作UI,所以出错;这里需要用到Dispatcher的invoke方法或者BeginInvoke方法来解决多线程更新UI问题(Invoke是同步执行,BeginInvoke是异步执行);不过首先须要获取当前AppDomain的Application对象,然后获取与此 DispatcherObject 关联的 Dispatcher。

示例如下:

MainWindow.xaml:

<Window x:Class="DispatcherLearning.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:DispatcherLearning"
        Title="MainWindow" Height="325" Width="502">
    <Grid>
        <Button Name="btn" Content="打开另一个窗体" HorizontalAlignment="Center" VerticalAlignment="Center"  Click="OpenWindow"/>
    </Grid>
</Window>

后台代码:

private void OpenWindow(object sender, RoutedEventArgs e)
        {
            Thread thread = new Thread(() =>
                {
                    Application.Current.Dispatcher.Invoke(new Action(() => { ChildWindow.show("打开了一个新窗体!"); }));
                });
            thread.IsBackground = true;
            thread.Start();
        }


 

ChildWindow类:

public partial class ChildWindow : Window
    {
        private ChildWindow()
        {
            InitializeComponent();
        }
        public static void show(string message)
        {
            ChildWindow childwin = new ChildWindow();
            childwin.messageBlock.Text = message;
            childwin.ShowDialog();
        }
    }


 

运行效果

WPF 如何在线程中打开子窗体


WPF 如何在线程中打开子窗体