2010-09-24 52 views
4

需要你的帮助。我有一个ListBox(带有虚拟化),它显示一个ScrollViewer。 我的列表框项目是可扩展的,虽然展开它们的高度可能会超过可见滚动区域。带列表框的WPF ScrollViewer

我遇到的问题是,当列表框项目超出可见滚动区域时 - 滚动跳转到下一个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> 

中 - 当然,我不能换我与另一个滚动列表框,因为它将可视关闭(这是非常虚弱无力的我)。

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

帮助!!!

吉利

回答

1

看看here (Bea Stollnitz)here(Dan Crevier);基本上你需要实现你自己的容器,它支持虚拟化和基于像素的滚动。您也可以查看this similar SO question了解更多详情或其他可能的选择。从VirtualizingStackPanel派生并修改滚动行为似乎是最好的选择。

+0

这将是荒谬的因为我花了这么多时间,让我的系统面临这样的风险。我们正在开发系统而不是控制。简单地将树视图调整为这一点更容易。我永远不会说永远不会,如果没有一个简单的解决方案 - 我们作为开发人员需要做必要的事情,但因为这里有更好的选择 - 我不明白为什么要冒险。 – Gilad 2012-04-16 11:28:02

+1

BTW:这是一个错误。微软应该照顾这一点。在基于像素的滚动和虚拟化之间不存在任何冲突。证明这一点的是,它在TreeView中工作得很好,由于异端结构他们没有选择。有人在那里懒惰...... :-) – Gilad 2012-04-16 11:31:03

+0

实际上,TreeView根本没有虚拟化,这是另一个令人头痛的问题。恰当地实施它是一个适当的痛苦。当然,这很困难,因为每个父项的项目都是由另一个ItemsControl表示的,并且使每个嵌入的ItemsControl知道最外层容器的大小和属性,以便虚拟化能够正常工作。这就是TreeView的性能真的吸引超过一小部分项目的原因。 – 2012-04-18 08:22:42

3

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

所以我试了这个,确实 - 它的工作原理!确保验证虚拟化是否有效,并测试了大约1000个项目,并在我的控件构造函数中设置了一个断点,并确保在我的视图滚动时调用它。

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

我创建了树型视图样式,使树型视图的外观和行为就像一个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属性扩展树视图项目数据模板以指示其选择状态。

希望这将有助于某人某天:-) 玩得开心!

吉利

2

这个问题仍然是即将在搜索引擎,所以两年后我会回答它。

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

如果你能目标4.5,然后只需添加到您的列表框:

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

为了什么在.NET 4.5(其中包括WPF 4.5新的东西)是新见http://msdn.microsoft.com/en-us/library/ms171868.aspx