Question

Une application WPF a une opération de chargement d'un contrôle utilisateur à partir d'un fichier séparé à l'aide de la méthode XamlReader.Load():

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

Le processus prend un certain temps en raison de la taille du fichier, l'interface utilisateur est donc figée pendant plusieurs secondes.

Pour garder l'application réactive, j'essaie d'utiliser un fil d'arrière-plan pour effectuer la partie de l'opération qui n'est pas directement impliquée dans la mise à jour de l'interface utilisateur.

En essayant d'utiliser BackgroundWorker, j'ai eu une erreur: Le thread appelant doit être STA, car de nombreux composants de l'interface utilisateur l'exigent

Alors, je suis allé autrement:

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

Cela ne résout rien car toutes les opérations chronophages restent dans le thread de l'interface utilisateur.

Quand j'essaye comme ça, je fais tout l'analyse en arrière-plan:

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

J'ai eu une exception: Le thread appelant ne peut pas accéder à cet objet car un autre thread le possède. (dans le UserControl chargé, il y a d'autres contrôles présents qui donnent peut-être le erreur)

Existe-t-il un moyen d'effectuer cette opération de manière à ce que l'interface utilisateur soit réactive?

Était-ce utile?

La solution

Obtenir le XAML pour charger un thread d'arrière-plan est essentiellement un non-starter.Les composants WPF ont une affinité de thread et ne sont généralement utilisables qu'à partir des threads qu'ils ont créés.Ainsi, le chargement sur un thread d'arrière-plan rendra l'interface utilisateur réactive, mais créera des composants qui ne pourront alors pas être connectés au thread d'interface utilisateur.

La meilleure option que vous avez ici est de diviser le fichier XAML en plus petits morceaux et de les charger de manière incrémentielle dans le thread d'interface utilisateur en veillant à permettre une pompe de message entre chaque opération de chargement.Utiliser éventuellement BeginInvoke sur l'objet Dispatcher pour planifier les chargements.

Autres conseils

Comme vous l'avez découvert, vous ne pouvez pas utiliser XamlReader.Load à moins que le thread ne soit STA et même si c'est le cas, vous devrez lui faire démarrer une pompe de message et canaliser tous les accès aux contrôles qu'il a créés à travers cela.C'est un moyen fondamental du fonctionnement de WPF et vous ne pouvez pas y aller contre.

Vos seules vraies options sont donc:

  1. Décomposez le XAML en petits morceaux.
  2. Démarrez un nouveau thread STA pour chaque appel Load.Après le retour de Load, le thread devra démarrer une boucle de message et gérer les contrôles qu'il a créés.Votre application devra prendre en compte le fait que différents contrôles appartiennent désormais à différents threads.

System.Xaml a une classe Xaml​Background​Reader, peut-être que vous pourriez la faire fonctionner pour vous.Analysez le XAML sur le thread d'arrière-plan mais créez les objets sur le thread d'interface utilisateur.

Vous pouvez appeler une méthode qui permet de donner le contrôle à un autre thread:

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

Il s'appelle .Freeze ()

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top