XamlReader.Load en un hilo en segundo plano.¿Es posible?
-
28-10-2019 - |
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?
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:
- Rompe el XAML en pedazos más pequeños.
- Comience un nuevo hilo STA para cada
Load
llamar. DespuésLoad
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.
No tengo una solución exacta, pero puede obtener alguna dirección de los siguientes enlaces.
http://www.codehosting.net/blog/blogengine/post/opening-wpf-windows-on-a-new-thread.aspx
http://prystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
System.Xaml tiene un XamlBackgroundReader
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 ()