8
votes

Virtualizing WPF Wraps Panel Problème

Il n'y a pas beaucoup d'options pour un panneau d'enveloppement virtualisé pour une utilisation dans WPF. Pour une raison ou une autre, MS a décidé de ne pas expédier une dans la bibliothèque standard.

Si quelqu'un pouvait être si audacieux de fournir une réponse source de la foule (et une explication) au premier élément de travail du projet CodePlex suivant, je l'apprécierais grandement:

http://virtualwrappanel.codeplex.com/workitem/1

Merci!


Résumé du problème:

J'ai récemment essayé d'utiliser le wrappanel virtualisé de ce projet et avez rencontré un bogue.

Étapes pour reproduire:

  1. Créer une liste de liste.
  2. Définissez le Wrappanel virtualisateur en tant que itemHost dans un modèle ListBoxPanel.
  3. Liez l'itemSource de la liste de liste à une collection observable.
  4. Supprimer un élément de la collection observable du support.

    Le debug.assert échoue (debug.assert (enfant == _children [Childindex], "mauvais enfant a été généré");) Dans la mesure de la mesure, et une exécution continue entraîne une exception null dans la méthode de nettoyage [voir Capture d'écran ci-jointe] .

    S'il vous plaît laissez-moi savoir si vous êtes capable de corriger cela.

    merci,

    ao


    code:

    http://virtualwrappanel.codeplex.com/sourcecontrol/list/changesets#

    Texte ALT HTTP: //virtualwrappanel.CodePlex. com / projet / téléchargement / attachmentdownload.ashx? ProjectName = Virtualwrappanel & WorkITemid = 1 & Fileattachmentiment = 138959


0 commentaires

3 Réponses :


0
votes

Tout d'abord, méfiez-vous en général, si vous retirez un objet d'une collection et que vous n'avez pas de référence, cet objet est mort au point de suppression. Ainsi, à la moindre appel refollantinalchildrange est illégal après la suppression, mais ce n'est pas le problème de base.

Deuxièmement, vous pourriez avoir une petite condition de course, même si ce n'est pas strictement multi-fileté. Avoir à vérifier (avec point d'arrêt) si ce gestionnaire d'événements réagit trop avec impatience - vous ne voulez pas que le gestionnaire d'événements fonctionne pendant que vous êtes toujours au milieu d'un retrait même s'il s'agit d'un seul élément.

Troisième, Vérifiez pour NULL après: xxx

et pour le premier essai change le code pour avoir une sortie gracieuse, ce qui signifie dans ce cas que GRACEFLEL continue - doit utiliser une boucle et des incréments dans La boucle pour pouvoir faire continue du tout.

Vérifiez également les internometchildren WHNE que vous voyez que NULL pour voir si ce chemin d'accès donne le même résultat que vos _ enfants, des données internes, NULL dans le même endroit)) > En outre, postez le projet d'échantillon entièrement compilable qui donne la reproduction (en tant que fichier zip) quelque part - réduit les informations aléatoires et permet à la PPL de construire / courir et de voir.

Parler d'hypothèses - chèque Quelle est votre "collection observable". Si vous retirez un article d'une collection, tout itérateur / énumérateur d'un état préalable de cette collection a le droit de lancer ou de donner des nulls et dans une interface utilisateur qui tente d'être trop intelligente, ayant Un itérateur rassis peut se produire facilement.


0 commentaires

4
votes

La méthode OnitemSchanged doit bien gérer correctement les paramètres ARGS. S'il vous plaît voir cette Question pour plus d'informations. Copier le code de cette question, vous devrez mettre à jour suremschanged comme:

protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args) {
    base.OnItemsChanged(sender, args);
    _abstractPanel = null;
    ResetScrollInfo();

    // ...ADD THIS...
    switch (args.Action) {
        case NotifyCollectionChangedAction.Remove:
        case NotifyCollectionChangedAction.Replace:
            RemoveInternalChildRange(args.Position.Index, args.ItemUICount);
            break;
        case NotifyCollectionChangedAction.Move:
            RemoveInternalChildRange(args.OldPosition.Index, args.ItemUICount);
            break;
    }
}


0 commentaires

8
votes

explication du problème

Vous avez demandé une explication de ce qui se passe mal aussi bien que des instructions sur la manière de le réparer. Jusqu'à présent, personne n'a expliqué le problème. Je le ferai.

Dans la liste de liste avec un virtualizingwrappanel Il existe cinq structures de données distinctes qui suivent des éléments de suivi, chacun de différentes manières:

  1. itemsSource: la collection originale (dans ce cas Observablecollection)
  2. COLLECTIONVIEW: Garde une liste séparée d'éléments triés / filtrés / groupés (uniquement si l'une de ces fonctionnalités est utilisée)
  3. itemContainergénérateur: suit la mappage entre les éléments et les conteneurs
  4. internalchildren: les conteneurs de pistes actuellement visibles
  5. wrappanelabstraction: pistes que les conteneurs apparaissent sur quelle ligne

    Lorsqu'un élément est retiré des élémentsSource, cette suppression doit être propagée via toutes les structures de données. Voici comment ça marche:

    1. Vous appelez supprimer () sur l'itemSource
    2. itemsSource supprime l'article et incendie sa collectionChanged qui est gérée par la collectionView
    3. CollectionView supprime l'élément (si le tri / filtrage / regroupement est utilisé) et tire sa collection de collection qui est traitée par l'élémentContainerergénérateur
    4. itemContainerergénerer met à jour sa cartographie, incendie ses élémentschangés qui sont gérés par VirtualizingPanel
    5. VirtualizingPanel appelle sa méthode virtuelle Onitemchanged mise en œuvre par VirtualizingWrappanel
    6. VirtualizingWrappanel rejette son wrappanelabstraction de sorte qu'il sera construit, mais il ne met jamais à jour les internomentres

      Pour cette raison, la collection internalchildren est désynchronisée avec les quatre autres collections, ce qui a conduit aux erreurs expérimentées.

      Solution au problème

      Pour résoudre le problème, ajoutez le code suivant n'importe où dans la méthode Onitemschanged de virtualisatricewrappanel: xxx

      Ceci maintient la collection internalchildren en synchronisation avec les autres structures de données.

      Pourquoi addInternalchild / insertinernalchild n'est pas appelé ici

      Vous pouvez vous demander pourquoi il n'y a pas d'appels à insérer Nationinernalchild ou Addinernalchild dans le code ci-dessus, et surtout pourquoi la manutention Remplacer et déplacer ne nécessite pas que nous ajoutons d'ajouter un nouvel élément pendant OnitemSchanged.

      La clé pour comprendre cela est de la manière que l'élémentContainergénération fonctionne.

      Lorsque l'élémentContainerergenerator reçoit un éventuel éventuel Tout immédiatement:

      1. itemContainergerAnerator supprime immédiatement l'élément de ses propres structures de données
      2. itemContaingerernerator déclenche l'événement IdemChanged. Le panneau devrait immédiatement retirer le conteneur.
      3. itemContaingerernérateur "n'apparegre" le conteneur en supprimant son DataContext

        D'autre part, l'élémentContaingerernérateur apprend qu'un élément est ajouté, tout est généralement différé:

        1. itemContainerergénerer ajoute immédiatement un "emplacement" pour l'élément de sa structure de données mais ne crée pas de conteneur
        2. itemContaingerernerator déclenche l'événement IdemChanged. Le panneau appelle InvalidateMeeeasure () [Ceci est fait par la classe de base - vous n'êtes pas obligé de le faire]
        3. Plus tard, lorsque la messeOverRide est appelée, le générateur.startat / MOVENNTEXT est utilisé pour générer les conteneurs d'élément. Tous les conteneurs nouvellement générés sont ajoutés aux internautes à cette époque.

          Ainsi, tous les déménagements de la collection internalchildren (y compris ceux qui font partie d'un déplacement ou de remplacement) doivent être effectués à l'intérieur suremschanged, mais des ajouts peuvent (et doivent) être différés jusqu'à la prochaine mesure.


2 commentaires

On dirait que Tom Goff a donné le code nécessaire pendant que je tapais ma réponse. Sa réponse est également correcte et est essentiellement la même chose que la mienne sans explication détaillée.


HI RAY - Nice résumé, vous avez eu mon vote. Un problème avec votre réponse est que la question n'est pas vraiment que la "collection interne de l'interruption est désynchronisée avec les quatre autres collections", mais je suis sûr que cela n'aide pas. La question sous-jacente est que les enfants réalisés ne sont pas «nettoyés». Si vous supprimez l'élément à l'index 10, l'élément de l'index 11 sera déplacé à l'index 10. Lorsque vous allez réaliser l'élément à l'index 10 (qui était auparavant à 11 heures), vous finirez par l'affirmation "incorrect enfant" a été généré ", étant donné que l'autre enfant n'a jamais été non réalisé.