2010-11-10 3 views
2

J'ai un peu de problème avec l'interruption de mon interface utilisateur, même si j'utilise un Dispatcher, et avant que je n'aille plus loin, je me demande si c'est la façon dont je gère la récupération de données.WPF Multithreading: Utilisation de Dispatcher mais l'interface utilisateur se bloque toujours?

En ce moment j'ai ma fenêtre principale créant un View et ViewModel. Puis, à l'intérieur d'un nouveau Thread (en utilisant un Dispatcher), il définit le View.DataContext = ViewModel. Un très grand ObservableCollection est créé paresseusement lorsque la reliure se déclenche et cause le ralentissement. Cependant, il semble que certains des autres éléments de l'interface utilisateur qui doivent apparaître avant que slowdow n'apparaissent pas.

private void ButtonClick(Object sender, RoutedEventArgs e) 
    { 
     MyView view = new MyView(); 
     MyViewModel vm = new MyViewModel(); 

     TabItem tabItem = new TabItem(); 
     tabItem.Header = "MyView"; 
     tabItem.Content = view; 

     MyTabCollection.Items.Add(tabItem); 

     Window working = new Working(); 
     working.Show(); 

     ThreadStart thread = delegate() 
     { 
      DispatcherOperation operation = Dispatcher.BeginInvoke(
       DispatcherPriority.Normal, 
       new Action(delegate() 
       { 
        view.DataContext = vm; 
        ((FrameworkElement)view.Parent).Focus(); 
        working.Close(); 
       } 
       ) 
      ); 
     }; 

     Thread theThread = new Thread(thread); 
     theThread.Start(); 
    } 

Cela dit, fondamentalement, il est censé créer une vue et un viewmodel, puis ajoutez la vue de la collection onglet je (ce qui signifie qu'il doit montrer le nouvel onglet au moins). Et, il devrait également montrer une fenêtre "Working ...". Après cela, un thread séparé est censé lier le ViewModel à la vue, se concentrer sur cet onglet et fermer la fenêtre de travail. Le problème est que la première partie ne montre pas jusqu'à ce que tout soit fait; L'onglet n'est pas affiché et la fenêtre de travail n'est pas affichée tant que le nouveau thread n'est pas terminé (ce qui provoque l'affichage/fermeture immédiate de la fenêtre de travail). Je suppose que cela pourrait avoir à faire avec la façon dont je récupère les données, mais je ne suis pas sûr. Voici la façon dont il le fait:

  1. Créer une vue
  2. Créer ViewModel
  3. Créer TabItem avec le contenu ensemble à la vue et ajouter le TabItem au TabCollection.
  4. Créer/Afficher la fenêtre "Working ..."
  5. Dispatcher: Définir View.DataContext = ViewModel. Cet événement déclenche les DataBindings, qui à leur tour récupèrent ObservableCollection. Puisque le CO est créé par Lazily, il est en train d'être créé (c'est le goulot d'étranglement). < - Est-ce que cela gâche mon thread/expéditeur séparé?
  6. Dispatcher: définir le focus sur l'onglet
  7. Fermer la fenêtre "Working ..."

Répondre

5

Tout votre fil supplémentaire est en train de faire est un autre appel marshalling au thread répartiteur. Vraisemblablement, vous voulez réellement travailler sur le fil supplémentaire, ou il n'y a aucun intérêt à le créer.

Idéalement, votre thread supplémentaire devrait extraire toutes les données de manière appropriée, vous laissant seulement pour réellement connecter le tout dans le fil du répartiteur. L'important est de décider quel travail vous devez faire sur le thread d'interface utilisateur et quel travail vous devez faire sur le thread d'arrière-plan.

+0

Je me sorta ce que vous dites ... mais si j'ai mes cours séparé comme ceci: View, ViewModel , Model et DAL (DataAccessLayer) Où dois-je lancer ce thread séparé pour que cela fonctionne réellement? –

+0

@myermian: Cela dépend vraiment du fonctionnement de votre code. Vous pouvez appeler votre DAL ou votre modèle avec un appel asynchrone pour * obtenir * les données, puis revenir au fil de l'interface utilisateur quand tout est là. –

2

Évidemment, votre analyse du problème est correcte. Votre modèle de vue charge paresseusement des données quand cela est nécessaire, et cela ne se produit pas avant le rappel de Dispatcher, à quel point vous êtes de nouveau sur le thread de l'interface utilisateur et tout est verrouillé.

À mon avis, la solution est de faire le filetage dans la couche d'accès aux données:

Pour les collections: Vous pouvez définir des collections spéciales qui renvoient uniquement les éléments qui ont déjà été chargées à partir de la source de données en amont, puis déclencheur chargement d'éléments supplémentaires sur un thread séparé lorsqu'un utilisateur s'abonne à INotifyCollectionChanged. Lorsque les éléments supplémentaires sont actifs, déclenchez les événements INotifyCollectionChanged. Lorsque INotifyCollectionChanged est désabonné, annulez toute charge en attente.

Pour les totaux et similaires: Même idée.Comme les données viennent dans les augmentations totales et les événements se produisent (automatiquement pour DependencyProperty ou en utilisant INotifyPropertyChanged). En outre, la couche de données doit avoir une propriété parallèle à chaque collection, somme ou autre valeur chargée en temps différé indiquant si elle est entièrement chargée ou non, ce qui permet à l'interface utilisateur de griser les sections qui ne sont pas entièrement chargées. Il est également pratique d'avoir un indicateur global de «chargement» quelque part qui peut être utilisé pour griser les sections de l'interface utilisateur lorsque quelque chose se charge (plus facile d'écrire l'interface utilisateur de cette façon).

Notez que parfois une opération doit se bloquer jusqu'à ce que les données réelles aient été récupérées. Je pense que la chose la plus simple dans ce cas est de fournir des méthodes dans la couche de données pour forcer les données à être chargées de manière synchrone.

+0

+1, j'ai utilisé le modèle de collection 'work-in-process' et ça marche bien. Vous voyez l'avantage de cette approche dans VS 2010 dans la boîte de dialogue Ajouter des références (dans VS 2008, il faudrait une éternité pour charger la liste des références disponibles dans le GAC) ainsi que dans la fenêtre de désinstallation du programme de Windows 7. Une chose à garder à l'esprit avec cette technique est que les callbacks INotifyCollectionChanged doivent être distribués dans le thread UI et que vous devez faire attention lorsque vous modifiez la collection. J'ai trouvé qu'il était utile d'avoir une collection 'buffer' purement utilisée pour 'transférer' les éléments dans le thread de l'interface utilisateur. –

0

Votre DispatcherPriority est réglé sur Normal - essayez de régler à Background car cela peut améliorer le rendu

Questions connexes