WPF Dispatcher.FromThread

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章