WPF ListBox avec une ListBox-UI virtualisation et défilement

Mon prototype affiche "documents" qui contiennent des "pages" qui sont représenté par des images miniatures. Chaque document peut avoir n'importe quel nombre de pages. Par exemple, il pourrait y avoir 1000 documents de 5 pages chacun, ou 5 documents de 1000 pages chacun, ou quelque part entre les deux. Les Documents ne contiennent pas d'autres documents. Dans mon balisage xaml j'ai un ListBox dont ItemsTemplate Références un interitemstemplate qui a aussi un ListBox. Je veux que la 2 niveaux d'éléments sélectionnés afin que je puisse effectuer divers opérations sur les documents ou les pages (supprimer, fusionner, déménager, etc.). The innerItemsTemplate ListBox utilise un WrapPanelItemsPanelTemplate.

pour le scénario où j'ai un grand nombre de documents avec quelques chaque page (par exemple, 10000 documents de 5 pages chacun), le défilement fonctionne très bien grâce à la virtualisation de L'interface utilisateur par VirtualizingStackPanel. Cependant, j'ai des problèmes si j'ai un grand nombre de pages. Document avec 1000 pages n'affichera qu'environ 50 à la fois (ce qui convient sur l'écran), et quand je scroll vers le bas, l'extérieur ListBox déplace le document suivant, le saut de l'950 pages qui n'étaient pas visibles. Parallèlement à cela, il n'y a pas de VirtualzingWrapPanel ainsi la mémoire de l'application augmente vraiment.

je me demande si je m'y prends de la bonne façon, surtout depuis il est un peu difficile à expliquer! Je voudrais être capable d'afficher 10000 documents de 1000 pages chacun (montrant seulement ce qui s'adapte sur l'écran), en utilisant la virtualisation de L'interface utilisateur, et aussi smooth défiler.

Comment puis-je m'assurer que le défilement se déplace à travers toutes les pages du document avant qu'il n'affiche le document suivant, et conserve toujours la virtualisation de L'interface utilisateur? La barre de défilement ne semble se déplacer que vers le document suivant.

semble-t-il logique de représenter "documents" et "pages"" - avec ma méthode actuelle d'utiliser un ListBox dans un ListBox?

j'apprécierais beaucoup vos idées. remercier.

26
demandé sur Adi Lester 2009-12-30 04:18:55

5 réponses

ici, La réponse est surprenante:

  • Si vous utilisez ItemsControl ou ListBox vous obtiendrez le comportement que vous rencontrez, où le contrôle scrolls "par article" donc vous sautez sur un document entier à la fois, mais
  • Si vous utilisez TreeView au lieu de cela, le contrôle se déroulera en douceur de sorte que vous pouvez faire défiler votre document et dans le suivant, mais il sera toujours en mesure de virtualiser.

je pense que la raison pour laquelle l'équipe du FPF a choisi ce comportement est que TreeViewcomporte généralement des éléments qui sont plus grands que la zone visible, alors qu'en général ListBoxes ne le sont pas.

dans tous les cas, il est trivial dans WPF de faire un TreeView regarder et agir comme un ListBox ou ItemsControl en modifiant simplement les ItemContainerStyle. C'est très simple. Vous pouvez lancer votre propre ou simplement copier sur le modèle approprié à partir du fichier de thème du système.

alors vous aurez quelque chose comme ceci:

<TreeView ItemsSource="{Binding documents}">
  <TreeView.ItemsPanel>
    <ItemsPanelTemplate>
      <VirtualizingStackPanel />
    </ItemsPanelTemplate>
  </TreeView.ItemsPanel>
  <TreeView.ItemContainerStyle>
    <Style TargetType="{x:Type TreeViewItem}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type TreeViewItem}">
            <ContentPresenter /> <!-- put your desired container style here  with a ContentPresenter inside -->
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </TreeView.ItemContainerStyle>
  <TreeView.ItemTemplate>
    <DataTemplate TargetType="{x:Type my:Document}">
      <Border BorderThickness="2"> <!-- your document frame will be more complicated than this -->
        <ItemsControl ItemsSource="{Binding pages}">
          ...
        </ItemsControl>
      </Border>
    </DataTemplate>
  </TreeView.ItemTemplate>
</TreeView>

prise en compte des pixels le défilement de ListBox et de style multiselect à travailler ensemble

si vous utilisez cette technique pour obtenir un scrolling basé sur un pixel, votre ItemsControl externe qui affiche les documents ne peut pas être une ListBox (parce que ListBox n'est pas une sous-classe de TreeView ou TreeViewItem). Ainsi, vous perdez tout le support Multi-Select de ListBox. Pour autant que je puisse le dire, il n'y a aucun moyen d'utiliser ces deux fonctionnalités ensemble sans inclure un peu de votre propre code pour une fonctionnalité ou l'autre.

Si vous besoin des deux ensembles de fonctionnalités dans le même contrôle, vous avez fondamentalement plusieurs options:

  1. implémenter la multi-sélection vous-même dans une sous-classe de TreeViewItem. Utilisez TreeViewItem au lieu de TreeView pour le contrôle externe, car il permet de sélectionner plusieurs enfants. Dans le modèle à L'intérieur de ItemsContainerStyle: ajouter une case à cocher autour du ContentPresenter, le modèle lient la case à cocher Isdelected, et le style de la case à cocher avec le modèle de contrôle pour obtenir le look vous vouloir. Ensuite, ajoutez vos propres gestionnaires d'événements de souris pour gérer Ctrl-Click et Shift-Click pour multiselect.

  2. implémenter vous-même la virtualisation en pixel-scrolled dans une sous-classe de VirtualizingPanel. C'est relativement simple, puisque la plus grande partie de la complexité de VirtualizingStackPanel est liée au défilement sans pixel et au recyclage des conteneurs. le Blog de Dan Crevier possède quelques informations utiles pour comprendre le VirtualizingPanel.

24
répondu Ray Burns 2009-12-31 04:03:50

notez que dans .Net 4.5 il n'y a pas besoin de ce hack car vous pouvez définir VirtualizingPanel.ScrollUnit= "Pixel".

pour que ce soit vraiment facile, voici quelques code:

public static class PixelBasedScrollingBehavior 
{
    public static bool GetIsEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsEnabledProperty);
    }

    public static void SetIsEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(IsEnabledProperty, value);
    }

    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(PixelBasedScrollingBehavior), new UIPropertyMetadata(false, HandleIsEnabledChanged));

    private static void HandleIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var vsp = d as VirtualizingStackPanel;
        if (vsp == null)
        {
            return;
        }

        var property = typeof(VirtualizingStackPanel).GetProperty("IsPixelBased",
                                                                     BindingFlags.NonPublic | BindingFlags.Instance);

        if (property == null)
        {
            throw new InvalidOperationException("Pixel-based scrolling behaviour hack no longer works!");
        }

        if ((bool)e.NewValue == true)
        {
            property.SetValue(vsp, true, new object[0]);
        }
        else
        {
            property.SetValue(vsp, false, new object[0]);
        }
    }
}

pour utiliser ceci sur une liste, par exemple, vous feriez:

<ListBox>
   <ListBox.ItemsPanel>
      <ItemsPanelTemplate>
         <VirtualizingStackPanel PixelBasedScrollingBehavior.IsEnabled="True">
          </VirtualizingStackPanel>
       </ItemsPanelTemplate>
   </ListBox.ItemsPanel>
</ListBox>
38
répondu Samuel Jack 2012-08-13 09:19:24

.NET 4.5 a maintenant le VirtualizingPanel.ScrollUnit="ScrollUnit" propriété. Je viens de convertir un de mes TreeViews en liste de diffusion et la performance était nettement meilleure.

Plus d'informations ici: http://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingpanel.scrollunit(v=vs. 110).aspx

14
répondu Boden Garman 2011-09-20 10:17:00

Cela a fonctionné pour moi. Il semble que quelques attributs simples le feront (.NET 4.5)

<ListBox            
    ItemsSource="{Binding MyItems}"
    VirtualizingStackPanel.IsVirtualizing="True"
    VirtualizingStackPanel.ScrollUnit="Pixel"/>
6
répondu rmirabelle 2017-02-14 08:02:25

permettez-moi de commencer cette réponse par une question: L'utilisateur doit-il voir chaque vignette dans chaque élément de la liste à tout moment?

si la réponse à cette question est "non", alors peut-être serait-il possible de limiter le nombre de pages visibles à l'intérieur du modèle d'item interne (étant donné que vous avez indiqué que le défilement fonctionne bien avec, disons, 5 pages) et d'utiliser un modèle "d'item choisi" séparé qui est plus grand et affiche toutes les pages pour ce document? Billy Hollis explique comment " pop " un article sélectionné dans une liste sur dnrtv épisode 115

0
répondu kiwipom 2009-12-30 07:31:16