2011-01-12 3 views
3

Dans mon application, j'ai besoin d'acquérir le contenu d'un ItemsCollection immédiatement après la modification de ItemsSource. Ou, à tout le moins, avant la possibilité que le contenu soit dessiné visuellement.Condition de concurrence possible avec WPF ItemsCollection.ItemContainerGenerator

J'ai testé quelque chose proche de ce qui suit:

void UserControl_Loaded(object sender, EventArgs eventArgs) { 
    this.itemsControl.ItemsSource = GetItemsSource(); 

    int ctrIndex = 0; 
    DependencyObject container; 
    while((container = this.itemsControl.ItemContainerGenerator. 
     ContainerFromIndex(ctrIndex++)) != null) { 

     DoSomething(VisualTreeHelper.GetChild(container, 0)); 
    } 
} 

Le problème est que, au point DoSomething est appelé, la valeur de VisualTreeHelper.GetChildrenCount(container) est 0. Si ce code est appelé à un moment ultérieur, par exemple en réponse à un tir d'événement Button.Click, VisualTreeHelper.GetChildrenCount est la valeur attendue et le code fonctionnera probablement.

PS. J'ai également essayé de conduire la boucle while à l'intérieur d'une fonction anonyme:

this.itemsControl.ItemContainerGenerator.ItemsChanged += (_sender, _ea) => { 
    int ctrIndex = 0; 
    DependencyObject container; 
    while((container = this.itemsControl.ItemContainerGenerator. 
     ContainerFromIndex(ctrIndex++)) != null) { 

     DoSomething(VisualTreeHelper.GetChild(container, 0)); 
    } 
}; 

Le comportement est identique, malheureusement.

modifier

Je ne peux pas croire combien de cerceaux vous devez sauter à travers pour le contenu généré.

Je crois que j'ai trouvé le premier moment que je peux capturer en toute sécurité les conteneurs générés. Cependant, je suis toujours en train d'augmenter zéro sur le contenu généré à l'intérieur de ces conteneurs. Respectez les consignes suivantes:

this.itemsControl.ItemContainerGenerator.StatusChanged += new EventHandler(StatusChanged); 

void StatusChanged(object sender, EventArgs e) { 
    var cg = this.itemsControl.ItemContainerGenerator; 
    if(cg.Status == GeneratorStatus.ContainersGenerated && cg.ContainerFromIndex(0) != null) { 
    DoStuff(); 
    } 
} 

Au est appelé le point DoStuff(), les contenants retournés de ContainerFromIndex ne sont pas nulles. Cependant, VisualTreeHelper.GetChildrenCount(container) est 0. J'aimerais toujours beaucoup savoir si quelqu'un a résolu cela.

+0

Pour les lecteurs, je ferai remarquer que je n'ai trouvé absolument aucun moyen fiable de référencer le contenu généré dans un ItemsControl. Évitez cela à tout prix. – Kivin

Répondre

0

Le problème est que vous ne pouvez pas itérer sur une collection pendant qu'elle est en cours de modification. Une façon de surmonter cela consiste à regarder l'objet ItemsSource directement après le remplissage des données, plutôt que d'essayer d'itérer sur ItemsSource sur le contrôle. Si vous utilisez un modèle MVVVM, vous devriez pouvoir renseigner une propriété de collection (celle que vous liez à ItemsSource de votre contrôle) dans votre ViewModel et inspectez-la une fois que les données sont renvoyées à partir de la base de données/service.

+0

À moins que je ne me trompe, regarder ItemsSource ne résoudra pas ce problème.Je suis sous l'hypothèse que ItemsSource sera == les données que j'applique à ItemsSource dans l'événement Control_Loaded. Ces données ne sont pas pertinentes après leur liaison à ItemsSource. – Kivin

3

Je rencontrais le même problème il y a quelques minutes. Une petite différence est, j'avais besoin des informations exactes de positionnement et de taille des conteneurs d'articles. J'ai essayé avec votre tentative d'écouter l'événement StatusChanged du ItemContainerGenerator et finalement découvert que tandis que le statut devenait ContainersGenerated et que les conteneurs étaient effectivement générés, ils n'étaient pas encore disposés. J'ai donc fait quelque chose de vraiment bourru. D'abord, je mis un drapeau, disons, _updatePending, alors que l'état du ItemContainerGenerator est devenu ContainersGenerated, puis je me suis occupé de l'événement LayoutUpdated du ItemsControl, qui tireraient assez souvent, pour vérifier le drapeau _updatePending et si les conteneurs d'éléments sont disposés:

var firstContainer = this.ItemsContainer.ItemContainerGenerator.ContainerFromIndex(0) as FrameworkElement; 
if (_updatePending 
    && firstContainer != null 
    && firstContainer.IsLoaded) 
{ 
    // do stuff 
    _updatePending = false; 
} 

Ceci est brutal mais en quelque sorte efficace.

Questions connexes