9

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<StreamReader>)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<DependencyObject>)FinishedReading,
           rootObject3);
  }

  void FinishedReading(DependencyObject rootObject)
  {            
    ContentControl displayPage = FindName("displayContentControl") as ContentControl;
    displayPage.Content = rootObject;
  } 

我遇到了一个异常:调用线程无法访问此对象,因为另一个线程拥有它。(在加载的 UserControl 中存在其他可能会出错的控件)

有什么方法可以使 UI 响应式地执行此操作?

4

5 回答 5

7

让 XAML 加载后台线程本质上是不可能的。WPF 组件具有线程关联性,并且通常只能从它们创建的线程中使用。因此,在后台线程上加载将使 UI 响应,但会创建无法插入 UI 线程的组件。

您在这里的最佳选择是将 XAML 文件分解成更小的部分,并在 UI 线程中增量加载它们,确保在每个加载操作之间允许消息泵。可能BeginInvokeDispatcher对象上使用来安排负载。

于 2011-03-22T17:25:10.943 回答
2

正如您所发现的,除非线程是 STA,否则您不能使用XamlReader.Load,即使它是,您也必须让它启动一个消息泵并汇集对其通过该线程创建的控件的所有访问。这是 WPF 工作方式的基本方式,您不能违背它。

所以你唯一真正的选择是:

  1. 将 XAML 分解成更小的部分。
  2. 为每个Load调用启动一个新的 STA 线程。返回后Load,线程将需要启动一个消息循环并管理它创建的控件。您的应用程序必须考虑到不同的控件现在由不同的线程拥有这一事实。
于 2011-03-22T17:29:08.723 回答
1

我没有确切的解决方案,但您可以从以下链接中获得一些指导。

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/

于 2011-03-22T19:13:05.090 回答
0

System.Xaml 有一个Xaml​Background​Reader类,也许你可以让它为你工作。在后台线程上解析 XAML,但在 UI 线程上构建对象。

于 2017-04-29T04:00:22.127 回答
-1

您可以调用允许将控制权交给另一个线程的方法:

http://msdn.microsoft.com/en-us/library/ms748331.aspx

它被称为 .Freeze()

于 2011-08-01T15:55:03.030 回答