好的,所以在我即将放弃并以某种方式学习如何忍受这个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属性扩展树视图项目数据模板以指示其选择状态。
希望这将有助于某人某天:-) 玩得开心!
吉利
这将是荒谬的因为我花了这么多时间,让我的系统面临这样的风险。我们正在开发系统而不是控制。简单地将树视图调整为这一点更容易。我永远不会说永远不会,如果没有一个简单的解决方案 - 我们作为开发人员需要做必要的事情,但因为这里有更好的选择 - 我不明白为什么要冒险。 – Gilad 2012-04-16 11:28:02
BTW:这是一个错误。微软应该照顾这一点。在基于像素的滚动和虚拟化之间不存在任何冲突。证明这一点的是,它在TreeView中工作得很好,由于异端结构他们没有选择。有人在那里懒惰...... :-) – Gilad 2012-04-16 11:31:03
实际上,TreeView根本没有虚拟化,这是另一个令人头痛的问题。恰当地实施它是一个适当的痛苦。当然,这很困难,因为每个父项的项目都是由另一个ItemsControl表示的,并且使每个嵌入的ItemsControl知道最外层容器的大小和属性,以便虚拟化能够正常工作。这就是TreeView的性能真的吸引超过一小部分项目的原因。 – 2012-04-18 08:22:42