VirtualizingStackPanel deja de funcionar cuando anulando la plantilla de control por defecto para ScrollViewer

StackOverflow https://stackoverflow.com/questions/4219768

Pregunta

Tengo un cuadro de lista con una gran cantidad de artículos que son caros de hacer. Sin embargo, el VirtualizingStackPanel se encarga de eso simplemente dando los elementos visibles. Quiero anular la plantilla de control para ScrollViewer ya que el defecto tiene rectángulo gris entre la barra de desplazamiento horizontal y vertical. He copiado el proporcionado por Microsoft ( ScrollViewer ControlTemplate ejemplo ) que no tienen el problema rectángulo gris.

Esta ControlTemplate embargo desactiva la virtualización dando a la altura infinita VirtualizingStackPanel. Eso significa que el VirtualizingStackPanel hará que todos los artículos, ya que cree que todos los elementos son visibles.

En el siguiente código de demostración que muestran 10000 artículos en un cuadro de lista. Verifico la cuestión mediante la comparación de ejecutarlo con el estilo ScrollViewer y sin ella. Con ella corre demostración muy lento, el cambio de tamaño se lleva a muchos segundos. Sin el estilo es muy rápido. salida que algo de información acerca de la VirtualizingStackPanel para probar mi punto:

Sin el estilo ScrollViewer (Comentario a cabo el estilo en el XAML):

  

ViewportHeight: 8
  ExtentHeight: 10000
  ActualHeight: 245
  IsVirtualizing: True
  VirtualizationMode: Estándar

Con el estilo ScrollViewer:

  

ViewportHeight: 0
  ExtentHeight: 0
  ActualHeight: 272,766.666666707
  IsVirtualizing: True
  VirtualizationMode: Estándar

¿Alguna idea de cómo escribir una plantilla de control para un ScrollViewer que no pierde el tiempo con la virtualización?

XAML:

<Window x:Class="VirtualTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">

    <Window.Resources>

        <Style x:Key="{x:Type ScrollViewer}" TargetType="{x:Type ScrollViewer}">
            <Setter Property="OverridesDefaultStyle" Value="true" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ScrollViewer}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
                            <ScrollContentPresenter Grid.Row="0" Grid.Column="0" />
                            <ScrollBar 
                                Name="PART_VerticalScrollBar" 
                                Grid.Row="0" Grid.Column="1" 
                                Value="{TemplateBinding VerticalOffset}" 
                                Maximum="{TemplateBinding ScrollableHeight}" 
                                ViewportSize="{TemplateBinding ViewportHeight}" 
                                Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" />
                            <ScrollBar 
                                Name="PART_HorizontalScrollBar" 
                                Orientation="Horizontal" 
                                Grid.Row="1" Grid.Column="0" 
                                Value="{TemplateBinding HorizontalOffset}" 
                                Maximum="{TemplateBinding ScrollableWidth}" 
                                ViewportSize="{TemplateBinding ViewportWidth}" 
                                Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Window.Resources>

    <Grid>
        <ListBox 
            ItemsSource="{Binding Numbers}"
            ScrollViewer.ScrollChanged="ListBox_ScrollChanged"
            Background="Orange">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Red" BorderThickness="2" Margin="5">
                        <TextBlock Text="{Binding .}" Width="400"/>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate> 
        </ListBox>

    </Grid>
</Window>

Código atrás:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace VirtualTest
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            DataContext = this;
        }

        public IEnumerable<double> Numbers
        {
            get
            {
                for (int i = 0; i < 10000; i++)
                {
                    yield return i;
                }
            }
        }

        private void ListBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            ListBox listBox = sender as ListBox;
            VirtualizingStackPanel virtualizingStackPanel = FindVirtualizingStackPanel(listBox);                             
            Debug.WriteLine("ViewportHeight: " + virtualizingStackPanel.ViewportHeight);
            Debug.WriteLine("ExtentHeight: " + virtualizingStackPanel.ExtentHeight);
            Debug.WriteLine("ActualHeight: " + virtualizingStackPanel.ActualHeight);
            Debug.WriteLine("IsVirtualizing: " + VirtualizingStackPanel.GetIsVirtualizing(virtualizingStackPanel));
            Debug.WriteLine("VirtualizationMode: " + VirtualizingStackPanel.GetVirtualizationMode(virtualizingStackPanel));
        }

        private VirtualizingStackPanel FindVirtualizingStackPanel(Visual visual)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
            {
                Visual child = VisualTreeHelper.GetChild(visual, i) as Visual;

                if (child != null)
                {
                    if (child is VirtualizingStackPanel)
                    {
                        return child as VirtualizingStackPanel;
                    }

                    VirtualizingStackPanel found = FindVirtualizingStackPanel(child);
                    if (found != null)
                    {
                        return found;
                    }
                }
            }

            return null;
        }
    }
}
¿Fue útil?

Solución

Este estilo ScrollViewer se copia de Blend 4 y se ve bien en la ventana de salida

  

ViewportHeight: 10
  ExtentHeight: 10000
  ActualHeight: 308
  IsVirtualizing: True
  VirtualizationMode: Estándar

<Style TargetType="{x:Type ScrollViewer}">
    <Style.Triggers>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
        </Trigger>
    </Style.Triggers>
    <Setter Property="Template" Value="{DynamicResource ScrollViewerControlTemplate1}"/>
</Style>
<ControlTemplate x:Key="ScrollViewerControlTemplate1" TargetType="{x:Type ScrollViewer}">
    <Grid x:Name="Grid" Background="{TemplateBinding Background}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
        <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
        <ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
        <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
    </Grid>
</ControlTemplate>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top