背景线程中的XamlReader.Load。 可能吗?
WPF应用程序具有使用XamlReader.Load()
方法从单独的文件加载用户控件的操作:
StreamReader mysr = new StreamReader(pathToFile); DependencyObject rootObject = XamlReader.Load(mysr.BaseStream) as DependencyObject; ContentControl displayPage = FindName("displayContentControl") as ContentControl; displayPage.Content = rootObject;
由于文件的大小,该过程需要一些时间,因此UI会冻结几秒钟。
为了保持应用程序响应,我尝试使用后台线程来执行在UI更新中不直接调用的操作部分。
当尝试使用BackgroundWorker
我收到一个错误: 调用线程必须是STA,因为许多UI组件都需要这个
所以,我走另一条路:
private Thread _backgroundThread; _backgroundThread = new Thread(DoReadFile); _backgroundThread.SetApartmentState(ApartmentState.STA); _backgroundThread.Start(); void DoReadFile() { StreamReader mysr3 = new StreamReader(path2); Dispatcher.BeginInvoke( DispatcherPriority.Normal, (Action)FinishedReading, mysr3); } void FinishedReading(StreamReader stream) { DependencyObject rootObject = XamlReader.Load(stream.BaseStream) as DependencyObject; ContentControl displayPage = FindName("displayContentControl") as ContentControl; displayPage.Content = rootObject; }
这解决了什么,因为所有耗时的操作都保留在UI线程中。
当我这样尝试时,在后台进行所有解析:
private Thread _backgroundThread; _backgroundThread = new Thread(DoReadFile); _backgroundThread.SetApartmentState(ApartmentState.STA); _backgroundThread.Start(); void DoReadFile() { StreamReader mysr3 = new StreamReader(path2); DependencyObject rootObject3 = XamlReader.Load(mysr3.BaseStream) as DependencyObject; Dispatcher.BeginInvoke( DispatcherPriority.Normal, (Action)FinishedReading, rootObject3); } void FinishedReading(DependencyObject rootObject) { ContentControl displayPage = FindName("displayContentControl") as ContentControl; displayPage.Content = rootObject; }
我有一个例外: 调用线程无法访问此对象,因为另一个线程拥有它。 (在加载的UserControl中还有其他控件可能会出错)
有没有办法以这样的方式执行此操作UI要响应?
让XAML加载后台线程本质上是一个非首发。 WPF组件具有线程亲和性,通常只能从创建它们的线程中使用。 因此,在后台线程上加载将使UI响应,但创建组件,然后无法插入UI线程。
您在这里拥有的最佳选择是将XAML文件分解为更小的部分,并在UI线程中逐步加载它们,确保在每次加载操作之间允许消息泵。 可能在Dispatcher
对象上使用BeginInvoke
来计划加载。
正如您所发现的那样,除非线程是STA,否则您不能使用XamlReader.Load
,即使它是,也必须让它启动消息泵并且通过它汇集对它创建的控件的所有访问权限。 这是WPF如何工作的基本方式,你不能反对它。
所以你唯一真正的选择是:
- 将XAML分解成更小的部分。
- 为每个
Load
调用启动一个新的STA线程。Load
返回后,线程将需要启动消息循环并管理它创建的控件。 您的应用程序必须考虑到不同的控件现在由不同的线程拥有的事实。
我没有确切的解决方案,但你可以从以下链接获得一些指示。
http://www.codehosting.net/blog/BlogEngine/post/Opening-WPF-Windows-on-a-new-thread.aspx
http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
System.Xaml有一个XamlBackgroundReader
类,也许你可以XamlBackgroundReader
为你工作。 在后台线程上解析XAML,但在UI线程上构建对象。