Dispatcher.FromThread 方法可以从线程中获得 Dispatcher ,如果此线程中操作了UI相关的对象,如窗体,控件等,那么它的返回值将不为null, 否则为null.
举个例子:
新建一个wpf应用:默认窗体 MainWindow 里放一个测试按钮。
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button Width="100" Height="40" Content="test" Click="Button_Click" />
</Grid>
</Window>
再新建一个窗体 Window1,放一个textblock:
<Window x:Class="WpfApp1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800">
<Grid>
<TextBlock x:Name="tb" Width="100" Height="40" />
</Grid>
</Window>
后台代码:
public partial class MainWindow : Window
{
private Window1 _win1;
private Thread _thread;
public MainWindow()
{
InitializeComponent();
Test();
}
public void Test()
{
_thread = new Thread(() =>
{
_win1 = new Window1();
_win1.ShowDialog();
});
_thread.SetApartmentState(ApartmentState.STA);//设置为 STA 才能操作UI
_thread.Start();
}
}
按钮测试代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
Dispatcher.Invoke(()=> {
_win1.tb.Text = "from thread";
});
}
这个时候,上面的代码是没法执行的,因为 _win1 根本不在主UI线程,执行会crash.
改为下面就正确了:
private void Button_Click(object sender, RoutedEventArgs e)
{
Dispatcher dispatcher = System.Windows.Threading.Dispatcher.FromThread(_thread);
dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
_win1.tb.Text = "from thread";
}));
}
附:如果使用的是 Task ,是没法直接设置 ApartmentState.STA 的,需要包装一下,如:
private async Task<bool> Test()
{
return await StartSTATask<bool>(() =>
{
_win1 = new Window1();
_win1.ShowDialog();
return true;
});
}
private Task<T> StartSTATask<T>(Func<T> func)
{
var tcs = new TaskCompletionSource<T>();
_thread = new Thread(() =>
{
try
{
tcs.SetResult(func());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
_thread.SetApartmentState(ApartmentState.STA);
_thread.Start();
return tcs.Task;
}