带有ListBox的WPF ScrollViewer

时间:2010-09-24 21:38:26

标签: wpf scroll

需要你的帮助。我有一个ListBox(带有虚拟),它显示ScrollViewer。 我的ListBox项目是可扩展的,并且在扩展时它们的高度可能超过可见滚动区域。

我正在解决的问题是当列表框项超出可见滚动区域时 - 滚动跳转到下一个ListBox项而不是简单地滚动视图。

检查此代码:

    <ListBox Grid.Row="1" Grid.Column="0" DataContext="{Binding SpecPackageSpecGroupListViewModel}" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling"
                     ItemContainerStyle="{StaticResource SpecPackageSpecGroupListBoxStyle}" ScrollViewer.IsDeferredScrollingEnabled="True" 
                     ItemsSource="{Binding SortedChildren}" ScrollViewer.CanContentScroll="True"
                     Background="Transparent"
                     BorderThickness="0" SelectionMode="Extended"
                     Margin="5,5,5,5">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Controls:SpecPackageSpecGroupControl/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

当然,我不能用另一个滚动条包装我的ListBox,因为它会关闭可视化(这对我来说非常无能)。

如果我将CanContentScroll设置为False,一切都按预期工作 - 但虚拟化停止工作。

HELP !!!

吉利

3 个答案:

答案 0 :(得分:3)

好的,就在我即将放弃并以某种方式学习如何忍受这个错误之前我碰到了一个帖子(我现在似乎无法找到),这表明TreeView确实支持基于像素的滚动( AKA物理滚动)没有关闭可视化。

所以我试过这个,确实 - 它有效!确保验证虚拟化是否正常工作,使用~1000项进行测试,并在我的控件构造函数上设置断点,并确保在滚动视图时调用它。

使用TreeView而不是ListBox的唯一缺点是TreeView似乎不支持多项选择(我需要) - 但实现这一点比实现ListBox的智能滚动要容易得多。

我为TreeViewItem创建了一个样式,使TreeViewItem的外观和行为与ListBoxItem一样,这实际上不是强制性的 - 但我更喜欢这样(除了基本样式有拉伸问题,我必须用样式修复的事实) )。基本上我删除了ItemsPresenter并仅使用ContentPresenter,因为我的数据不是分层的:

    <Style x:Key="MyTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="SnapsToDevicePixels" Value="true"/>
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Setter Property="VerticalContentAlignment" Value="Stretch"/>
        <Setter Property="OverridesDefaultStyle" Value="true"/>    
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TreeViewItem}">
                    <Border Name="myBorder" 
                        SnapsToDevicePixels="true" 
                        CornerRadius="0,0,0,0" 
                        VerticalAlignment="Stretch" 
                        HorizontalAlignment="Stretch"
                        BorderThickness="0"
                        BorderBrush="Transparent"
                        Height="Auto"
                        Margin="1,1,1,3" 
                        Background="Transparent">
                        <ContentPresenter Grid.Column="1" x:Name="PART_Header" HorizontalAlignment="Stretch" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

现在 - 我唯一要做的就是实现多选树视图。 可能有不同的方法来实现这种行为,我采用了ViewModel方法。

从TreeView派生我创建了一个新的MultiSelectionTreeView:

public class MultiSelectionTreeView : TreeView
{
    private static bool CtrlPressed
    {
        get
        {
            return Keyboard.IsKeyDown(Key.LeftCtrl);
        }
    }

    protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e)
    {
        base.OnSelectedItemChanged(e);

        var previouseItemViewModel = e.OldValue as IMultiSelectionTreeViewItemViewModel;
        if (previouseItemViewModel != null)
        {
            if (!CtrlPressed)
                previouseItemViewModel.IsSelected = false;
        }                        

        var newItemViewModel = e.NewValue as IMultiSelectionTreeViewItemViewModel;
        if (newItemViewModel != null)
        {
            if (!CtrlPressed)
                newItemViewModel.ClearSelectedSiblings();
            newItemViewModel.IsSelected = true;
        }                
    }
}

其中IMultiSelectionTreeViewItemViewModel如下:

public interface IMultiSelectionTreeViewItemViewModel
{
    bool IsSelected { get; set; }
    void ClearSelectedSiblings();
}

当然 - 现在我有责任处理所选项目的呈现方式 - 在我的情况下,它是由于我的树视图项目有自己的DataTemplate,它有选择的指示。 如果不是这种情况并且您需要它,只需根据其视图模型IsSelected属性扩展树视图项数据模板以指示其选择状态。

希望有一天这会有所帮助:-) 玩得开心!

吉利

答案 1 :(得分:2)

这个问题仍在搜索引擎中出现,所以我会在2年后回答。

WPF 4.5现在支持基于像素的虚拟化面板。

如果您可以定位4.5,那么只需将其添加到ListBox:

<Setter Property="VirtualizingPanel.ScrollUnit" Value="Pixel"/>

对于.NET 4.5中的新功能(包括WPF 4.5中的新内容),请参阅http://msdn.microsoft.com/en-us/library/ms171868.aspx

答案 2 :(得分:1)

看看here (Bea Stollnitz)here(Dan Crevier);基本上你需要实现自己的容器,支持虚拟化和基于像素的滚动。您还可以查看this similar SO question以获取更多详细信息或可能的替代方案。从VirtualizingStackPanel派生并修改滚动行为似乎是最好的选择。