C# 加载到后台线程中。可能吗?

C# 加载到后台线程中。可能吗?,c#,wpf,multithreading,xamlreader,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 Conten

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;
    }
私有线程\u后台线程;
_backgroundThread=新线程(DoReadFile);
_backgroundThread.SetApartmentState(ApartmentState.STA);
_backgroundThread.Start();
void DoReadFile()
{
StreamReader mysr3=新的StreamReader(路径2);
调度程序。开始启动(
调度员优先权,正常,
(动作)完成恐惧,
mysr3);
}
无效完成恐惧(StreamReader流)
{            
DependencyObject rootObject=XamlReader.Load(stream.BaseStream)作为DependencyObject;
ContentControl displayPage=FindName(“displayContentControl”)作为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;
  } 
私有线程\u后台线程;
_backgroundThread=新线程(DoReadFile);
_backgroundThread.SetApartmentState(ApartmentState.STA);
_backgroundThread.Start();
void DoReadFile()
{
StreamReader mysr3=新的StreamReader(路径2);
DependencyObject rootObject3=XamlReader.Load(mysr3.BaseStream)作为DependencyObject;
调度程序。开始启动(
调度员优先权,正常,
(动作)完成恐惧,
rootObject3);
}
无效完成恐惧(DependencyObject根对象)
{            
ContentControl displayPage=FindName(“displayContentControl”)作为ContentControl;
displayPage.Content=rootObject;
} 
我遇到一个异常:调用线程无法访问此对象,因为另一个线程拥有它。(在加载的UserControl中,存在其他可能导致错误的控件)


是否有任何方法可以执行此操作,从而使UI具有响应性

让XAML加载一个后台线程基本上是不可能的。WPF组件具有线程亲缘关系,一般来说,只能从创建它们的线程中使用。因此,在后台线程上加载将使UI响应,但会创建无法插入UI线程的组件


这里最好的选择是将XAML文件分解成更小的部分,并在UI线程中增量加载它们,确保在每次加载操作之间都有消息泵。可能使用
Dispatcher
对象上的
BeginInvoke
来调度负载

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

因此,您唯一的实际选择是:

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

  • 我没有确切的解决方案,但你可以从下面的链接得到一些方向


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


    它被称为.Freeze()

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

    使用backgroundworker,确保如果要修改(设置/添加)任何不在范围内的对象,而不是backgroundworker所在的线程中的对象,您使用的func/action或委托有效,而不尝试在backgroundworker线程中设置它。如果有任何事情做了你的工作而不是backgroundworker,那么当你完成时,在OnComplete事件/方法中获取结果(例如result),并在UI线程中更新你的对象。