Хорошо, так что перед тем, как я собирался сдаться и каким-то образом научиться жить с этой ошибкой, я наткнулся на пост (который я не могу найти сейчас), который предполагает, что TreeView поддерживает прокрутку на основе пикселей (AKA Physical Scrolling) без отключения визуализации.
Итак, я попробовал это и действительно - это работает!Убедитесь, что виртуализация работает, протестировано с ~ 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 модели представления.
Надеюсь, это когда-нибудь кому-нибудь поможет :-) Удачи!
Гили