Vraag WPF ListBox met een ListBox - UI-virtualisatie en scrollen


Mijn prototype geeft "documenten" weer die "pagina's" bevatten vertegenwoordigd door miniatuurafbeeldingen. Elk document kan hebben een onbeperkt aantal pagina's. Bijvoorbeeld, er kan zijn 1000 documenten met elk 5 pagina's of 5 documenten met 1000 pagina's elk, of ergens ertussen. Documenten bevatten geen andere documenten. In mijn xaml-opmaak heb ik een ListBox, van wie ItemsTemplate  verwijst naar een innerItemsTemplate die ook een ListBox. ik wil de 2 niveaus van geselecteerde items zodat ik verschillende bewerkingen kan uitvoeren op documenten of pagina's (verwijderen, samenvoegen, verplaatsen naar nieuwe locatie, enz.). De innerItemsTemplate ListBox gebruikt een WrapPanel als de ItemsPanelTemplate.

Voor het scenario waarin ik een groot aantal documenten heb met een paar pagina's elk (zeg, 10000 documenten met elk 5 pagina's), het scrollen werkt geweldig dankzij de UI-virtualisatie door de VirtualizingStackPanel. Ik heb echter problemen als ik een groot aantal pagina's heb. Een document met 1000 pagina's worden slechts ongeveer 50 tegelijk weergegeven (wat er op het scherm past) en wanneer ik naar beneden schuif, de buitenste ListBox gaat naar het volgende document en slaat de 950 over pagina's of zo die niet zichtbaar waren. Samen met dat is er geen VirtualzingWrapPanel dus het app-geheugen neemt echt toe.

Ik vraag me af of ik dit op de juiste manier aanpakt, vooral aangezien het een beetje moeilijk uit te leggen is! Ik zou graag kunnen weergeven 10000 documenten met elk 1000 pagina's (alleen zichtbaar wat op het scherm past), gebruikmakende van UI-virtualisatie, en ook soepel scrollen.

Hoe kan ik ervoor zorgen dat het scrollen door alle pagina's in het document gaat voordat het het volgende document weergeeft en nog steeds UI-virtualisatie behoudt? De schuifbalk lijkt alleen naar het volgende document te gaan.

Lijkt het logisch om "documenten" en "pagina's" te vertegenwoordigen - met mijn huidige methode om a te gebruiken ListBox binnen een ListBox?

Ik zou graag alle ideeën die je hebt op prijs stellen. Dank je.


26
2017-12-30 01:18


oorsprong


antwoorden:


Het antwoord is hier verrassend:

  • Als je gebruikt ItemsControl of ListBox je krijgt het gedrag dat je ervaart, waarbij het besturingselement "op item" schuift, zodat je in één keer over een heel document springt, MAAR
  • Als je gebruikt TreeView in plaats daarvan schuift het besturingselement soepel, zodat u door uw document en naar het volgende kunt bladeren, maar het nog steeds in staat zal zijn om te virtualiseren.

Ik denk dat de reden dat het WPF-team voor dit gedrag heeft gekozen, dat is TreeViewheeft gewoonlijk items die groter zijn dan het zichtbare gebied, terwijl dat gebruikelijk is ListBoxes niet.

In ieder geval is het triviaal in WPF om een TreeView kijk en gedraag je als een ListBox of ItemsControl door simpelweg de ItemContainerStyle. Dit is heel eenvoudig. U kunt uw eigen sjabloon of alleen de juiste sjabloon kopiëren vanuit het systeemthemabestand.

Dus je zult zoiets hebben als dit:

<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>

Schuiven op pixelbasis en multiselect in ListBox-stijl werken samen

Als u deze techniek gebruikt om op pixels gebaseerd scrollen te krijgen, kan uw outer ItemsControl die de documenten toont geen ListBox zijn (omdat ListBox geen subklasse is van TreeView of TreeViewItem). Zo verlies je alle ondersteuning voor multiselect van ListBox. Voor zover ik kan nagaan, is er geen manier om deze twee functies samen te gebruiken zonder een deel van uw eigen code op te nemen voor de ene functie of de andere.

Als u beide functies in hetzelfde besturingselement nodig hebt, hebt u in feite verschillende opties:

  1. Implementeer multi-selectie zelf in een subklasse van TreeViewItem. Gebruik TreeViewItem in plaats van TreeView voor het buitenste besturingselement, omdat hiermee meerdere kinderen kunnen worden geselecteerd. In de sjabloon in ItemsContainerStyle: voeg een CheckBox toe rond de ContentPresenter, bind de sjabloon het CheckBox aan IsSelected en style het CheckBox met controlesjabloon om de gewenste look te krijgen. Voeg vervolgens uw eigen event-handlers toe om Ctrl-Click en Shift-Click voor multiselect te verwerken.

  2. Implementeer zelf pixel-scrolled virtualisatie in een subklasse van VirtualizingPanel. Dit is relatief eenvoudig, omdat de meeste complexiteit van VirtualizingStackPanel te maken heeft met niet-pixel scrollen en containerrecycling. Dan Crevier's Blog heeft enkele nuttige informatie voor het begrijpen van VirtualizingPanel.


24
2017-12-30 08:17



Het is mogelijk om vlotte scrolling VirtualizingStackPanels in WPF 4.0 te bereiken zonder virtualisatie op te offeren als u bereid bent reflectie te gebruiken om toegang te krijgen tot de privéfunctionaliteit van het VirtualizingStackPanel. Het enige dat u hoeft te doen is de private IsPixelBased eigenschap van het VirtualizingStackPanel op true te zetten.

Merk op dat in .Net 4.5 deze hack niet nodig is omdat je VirtualizingPanel.ScrollUnit = "Pixel" kunt instellen.

Om het echt gemakkelijk te maken, hier is wat 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]);
        }
    }
}

Om dit in een ListBox te gebruiken, zou u bijvoorbeeld doen:

<ListBox>
   <ListBox.ItemsPanel>
      <ItemsPanelTemplate>
         <VirtualizingStackPanel PixelBasedScrollingBehavior.IsEnabled="True">
          </VirtualizingStackPanel>
       </ItemsPanelTemplate>
   </ListBox.ItemsPanel>
</ListBox>

38
2018-03-26 15:57



.NET 4.5 heeft nu de VirtualizingPanel.ScrollUnit="ScrollUnit" eigendom. Ik heb zojuist een van mijn TreeViews geconverteerd naar een ListBox en de prestaties waren merkbaar beter.

Meer informatie hier: http://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingpanel.scrollunit(v=vs.110).aspx


13
2017-09-20 10:17



Dit werkte voor mij. Het lijkt erop dat een paar eenvoudige attributen het doen (.NET 4.5)

<ListBox            
    ItemsSource="{Binding MyItems}"
    VirtualizingStackPanel.IsVirtualizing="True"
    VirtualizingStackPanel.ScrollUnit="Pixel"/>

5
2018-02-14 08:02



Staat u mij toe om dit antwoord vooraf in te vullen met een vraag: Moet de gebruiker elke miniatuur in elk item in de lijst te allen tijde zien?

Als het antwoord op die vraag 'nee' is, zou het misschien mogelijk zijn om het aantal zichtbare pagina's binnen de innerlijke itemsjabloon te beperken (aangezien u hebt aangegeven dat het scrollen goed werkt met bijvoorbeeld 5 pagina's) en een afzonderlijke 'geselecteerde item'-sjabloon die groter is en alle pagina's voor dat document weergeeft? Billy Hollis legt uit hoe je een geselecteerd item in een listbox op dnrtv kunt 'knallen' aflevering 115


0
2017-12-30 07:31