2009-05-08 15 views
2

Quelque chose d'étrange se passe avec ObservableCollection.ObservableCollection et le problème de génération de DataTemplate ListBoxItem

J'ai le code suivant:

private readonly ObservableCollection<DisplayVerse> _display; 
private readonly ListBox _box; 

    private void TransferToDisplay() 
    { 
     double elementsHeight = 0; 

     _display.Clear(); 

     for (int i = 0; i < _source.Count; i++) { 
      DisplayVerse verse = _source[i]; 
      _display.Add(verse); 
      elementsHeight += CalculateItemsHeight(i); 
      if (elementsHeight + Offset > _box.ActualHeight) { 
       _display.RemoveAt(_display.Count - 1); 
       break; 
      } 
     } 
     MessageBox.Show(elementsHeight.ToString()); 
    } 

    private double CalculateItemsHeight(int index) 
    { 
     ListBoxItem lbi = _box.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem; 
     return lbi != null ? lbi.ActualHeight : 0; 
    } 

Ce que je suis en train de faire ici est de contrôler le nombre d'éléments vont dans le _display ObservableCollection. Maintenant, dans cette boucle for, vous pouvez voir que les éléments sont ajoutés jusqu'à ce que la hauteur totale des éléments (+ offset) soit supérieure à la liste elle-même. Maintenant, cela est étrange, les élémentsHeight est égal à 0 après ceci pour la boucle. (CalculateItemsHeight renvoie 0 dans tous pour les itérations de boucle même si le lbi n'est pas nul) Il semble que les éléments d'interface utilisateur définis dans le datatemplate ne sont pas créés ...

Pourtant.

Maintenant, si je mets des MessageBox après le _display.Add (vers), vous pouvez voir que le CalculateItemsHeight retourne réellement la hauteur d'un élément.

for (int i = 0; i < _source.Count; i++) { 
    DisplayVerse verse = _source[i]; 
    _display.Add(verse); 
    MessageBox.Show("pause"); // <----- PROBLEM? 
    elementsHeight += CalculateItemsHeight(i); 
    if (elementsHeight + Offset > _box.ActualHeight) { 
     _display.RemoveAt(_display.Count - 1); 
     break; 
    } 
} 
MessageBox.Show(elementsHeight.ToString()); 

Après avoir modifier la boucle comme représenté, le dernier MessageBox montre en fait la hauteur réelle pour tous les éléments transformés.

Ma question est - quand les éléments de l'interface utilisateur sont-ils réellement créés? Il semble que cela a été fait quelque part pendant l'affichage de MessageBox. Ce comportement est assez étrange pour moi, peut-être qu'il a quelque chose à voir avec le threading, pas sûr.

Ajout à l'affichage _Display ObservableCollection crée évidemment un élément immédiatement, mais pas ses éléments visuels (ils sont cependant ajoutés par la suite, je ne sais pas exactement quand). Comment puis-je faire ce même comportement sans avoir à ouvrir la boîte de message?

Répondre

0

SOLVED

Cela crée effet quelque peu vacillante pour une fraction de seconde (comme si les éléments de chargement, un par un), mais convient réellement à mes besoins.

Le but est d'actualiser l'interface utilisateur d'un élément avant de récupérer sa hauteur.

J'ai créé une méthode d'extension:

public static void RefreshUI(this DependencyObject obj) 
    { 
     obj.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Loaded, (Action)delegate { }); 
    } 

Et puis avant de récupérer la hauteur, je rafraîchir l'interface utilisateur.

private double CalculateItemsHeight(int index) 
    { 
     ListBoxItem lbi = _box.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem; 
     if (lbi != null) { 
      lbi.RefreshUI(); 
      return lbi.ActualHeight; 
     } 
     return 0; 
    } 
0

Le moteur de mise en page wpf n'aura pas fait l'objet d'une mise en page et n'organisera pas de passage de sorte que vos listbox n'auront pas encore reçu une taille. S'en tenir dans la boîte de message permettra aux threads d'arrière-plan qui le font. Essayez de forcer un appel à Mesurer() sur vos articles avant de regarder leur taille.

+0

Non, appeler Measure() n'a pas résolu le problème. Je suppose que je devrais tempérer avec le fil de l'interface utilisateur, mais je ne sais pas quelle partie à appeler sur Thread UI ou comment. –

1

En fait, je cherchais à obtenir ce travail et j'ai trouvé le « .UpdateLayout() », ce qui fonctionne parfaitement pour moi. Je me rends compte que vous faites vertical et horizontal que je fais, mais voici mon code, il est assez simple:

for (int i = 0; i < listOfItems.ItemsIn.Count; ++i) 
    { 
     //CalculateItemsHeight(i); 

     ListBoxItem abc = (lb.ItemContainerGenerator.ContainerFromItem(lb.Items[i]) as ListBoxItem); 
     abc.UpdateLayout(); 
     totalWidth += abc.ActualWidth; 
    } 

Espérons que cela aide!