好的,在我准备放弃并学会如何处理这个错误之前,我遇到了一篇文章(现在似乎无法找到),它建议TreeView支持基于像素的滚动(也称为物理滚动),而不需要关闭可视化。
所以我尝试了一下,确实有效!确保验证了虚拟化工作,测试了大约1000个项目,并在我的控件构造函数上设置了断点,并确保在滚动我的视图时调用它。
与ListBox相比,使用TreeView唯一的缺点是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,这个DataTemplate中指示了它的选择状态。
如果这不是你的情况,并且你需要它,请简单地扩展你的树形视图项数据模板来指示其选择状态,根据其视图模型的IsSelected属性。
希望这将在某一天帮助到别人 :-) 祝愉快!
Gili