背景线程中的XamlReader.Load。可能吗?

时间:2011-03-22 17:20:07

标签: c# wpf multithreading xamlreader

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要响应?

5 个答案:

答案 0 :(得分:7)

让XAML加载后台线程本质上是一个非首发。 WPF组件具有线程亲和性,通常只能从创建它们的线程中使用。因此,在后台线程上加载会使UI响应,但会创建无法插入UI线程的组件。

这里你最好的选择是将XAML文件分解成更小的部分,然后在UI线程中逐步加载它们,确保在每次加载操作之间允许消息泵。可能在BeginInvoke对象上使用Dispatcher来安排加载。

答案 1 :(得分:2)

正如您所发现的那样,除非线程是STA,否则您不能使用XamlReader.Load,即使它是,也必须让它启动一个消息泵并将所有通过它创建的控件汇集到漏斗状态。这是WPF工作方式的基本方法,你不能反对它。

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

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

答案 2 :(得分:1)

答案 3 :(得分:0)

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

答案 4 :(得分:-1)

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

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

它被称为.Freeze()