覆盖ScrollViewer的默认控件模板时,VirtualizingStackPanel停止工作

时间:2010-11-18 21:28:43

标签: wpf controltemplate scrollviewer virtualizingstackpanel

我有一个包含大量项目的列表框,这些项目的渲染成本很高。但是VirtualizingStackPanel只通过渲染可见项来处理这个问题。我想覆盖ScrollViewer的控件模板,因为默认的控件模板在水平和垂直滚动条之间有灰色矩形。我刚刚复制了Microsoft(ScrollViewer ControlTemplate Example)提供的没有灰色矩形问题的那个。

然而,这个控制模板通过给予VirtualizingStackPanel无限高度来禁用虚拟化。这意味着VirtualizingStackPanel将呈现所有项目,因为它认为所有项目都是可见的。

在下面的演示代码中,我在列表框中显示了10000个项目。我通过比较运行它与ScrollViewer样式并且没有它来验证问题。有了它,演示运行速度非常慢,调整大小需要几秒钟。如果没有这种风格,那就非常快。我输出一些关于VirtualizingStackPanel的信息来证明我的观点:

没有ScrollViewer样式(注释掉XAML中的样式):

  

ViewportHeight:8
  ExtentHeight:10000
  ActualHeight:245
  IsVirtualizing:真实   VirtualizationMode:标准

使用ScrollViewer样式:

  

ViewportHeight:0
  ExtentHeight:0
  ActualHeight:272766.666666707
  IsVirtualizing:真实   VirtualizationMode:标准

任何想法如何为ScrollViewer编写一个不会影响虚拟化的控件模板?

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>

代码背后:

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

1 个答案:

答案 0 :(得分:3)

此ScrollViewer样式从Blend 4复制,在输出窗口中看起来很好

  

ViewportHeight:10
  ExtentHeight:10000
  ActualHeight:308
  IsVirtualizing:真实   VirtualizationMode:标准

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