Pregunta

Una aplicación WPF tiene una operación de cargar un control de usuario desde un archivo separado usando XamlReader.Load() método:

StreamReader mysr = new StreamReader(pathToFile);
DependencyObject rootObject = XamlReader.Load(mysr.BaseStream) as DependencyObject;
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;

El proceso lleva algún tiempo debido al tamaño del archivo, por lo que la interfaz de usuario se congela durante varios segundos.

Para mantener la capacidad de respuesta de la aplicación, intento utilizar un subproceso en segundo plano para realizar la parte de la operación que no está directamente relacionada con la actualización de la interfaz de usuario.

Al intentar utilizar BackgroundWorker Recibí un error: El hilo de llamada debe ser STA, porque muchos componentes de la interfaz de usuario lo requieren

Entonces, tomé otro camino:

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

Esto no resuelve nada porque todas las operaciones que consumen mucho tiempo permanecen en el subproceso de la interfaz de usuario.

Cuando intento así, hago todo el análisis en segundo plano:

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

Recibí una excepción: El hilo que llama no puede acceder a este objeto porque lo posee un hilo diferente. (en el UserControl cargado hay otros controles presentes que pueden dar el error)

¿Hay alguna forma de realizar esta operación de tal manera que la interfaz de usuario responda?

¿Fue útil?

Solución

Hacer que el XAML cargue un hilo de fondo A es esencialmente un no arranque. Los componentes de WPF tienen afinidad de subprocesos y, en general, se pueden usar solo utilizables de los hilos que se crean uno. Por lo tanto, cargar en un hilo de fondo hará que la interfaz de usuario respondiera pero creará componentes que luego no se pueden conectar al hilo de la interfaz de usuario.

La mejor opción que tiene aquí es dividir el archivo XAML en piezas más pequeñas y cargarlos incrementalmente en el hilo de la interfaz de usuario asegurándose de permitir una bomba de mensaje entre cada operación de carga. Posiblemente usando BeginInvoke sobre el Dispatcher OBJETO para programar las cargas.

Otros consejos

Como descubriste, no puedes usar XamlReader.Load A menos que el hilo sea STA e incluso si lo es, deberá hacer que inicie una bomba de mensaje y canalizar todo el acceso a los controles que creó a través de eso. Esta es una forma fundamental de cómo funciona WPF y no puedes ir en contra de él.

Entonces, sus únicas opciones reales son:

  1. Rompe el XAML en pedazos más pequeños.
  2. Comience un nuevo hilo STA para cada Load llamar. Después Load Devuelve, el hilo deberá iniciar un bucle de mensaje y administrar los controles que creó. Su aplicación tendrá que tener en cuenta el hecho de que los diferentes controles ahora son propiedad de diferentes hilos.

System.Xaml tiene un Xaml​Background​Reader clase, tal vez puedas hacer que eso funcione para ti.Analice el XAML en el subproceso en segundo plano pero cree los objetos en el subproceso de la interfaz de usuario.

Puede llamar a un método que permita dar control a otro hilo:

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

Se llama .Freeze ()

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top