Я бы хотел выполнить отложенную загрузку TreeViewItem в WPF. Я уже знаю общий ответ о добавлении некоторых «виртуальных» элементов в ItemsSource и о событии Expanded для обмена этим виртуальным элементом с реальным списком.
Проблема с исходным TreeViewItem заключается в том, что он всегда обращается к исходному списку, который я не хочу, по крайней мере, для первого уровня, но я не могу разрешить это поведение, так как создание второго уровня вызывает большой трафик в моем случае .
Изменение свойства HasItems на самом деле не работает, поскольку свойство HasItems глубоко закодировано в TreeViewItem. Поэтому я решил добавить LazyItemsSource к TreeViewItem (DynamicTreeViewItem), и если кто-то расширяет узел, LazyItemsSource назначается ItemsSource.
Проблема в том, что мне нужно управлять иерархией через HierarchicalDataTemplate. Вместо назначения ItemsSource в шаблоне я хотел назначить LazyItemsSource, но как-то это невозможно. Отображаемая ошибка:
ошибка MC4104: свойство 'LazyItemsSource' не может быть установлено в качестве элемента свойства в шаблоне. Только триггеры и раскадровки допускаются в качестве элементов свойств. Строка 10, позиция 79.
Но я понятия не имею, почему компилятор жалуется на это.
Вот исходный код:
using System.Collections;
using System.Windows;
using System.Windows.Controls;
namespace WpfApp8
{
public class DynamicTreeView : TreeView
{
protected override DependencyObject GetContainerForItemOverride()
{
return new DynamicTreeViewItem();
}
}
public class DynamicTreeViewItem : TreeViewItem
{
public static readonly DependencyProperty LazyItemsSourceProperty
= DependencyProperty.Register("LazyItemsSource", typeof(IEnumerable), typeof(ItemsControl),
new FrameworkPropertyMetadata((IEnumerable)null,
new PropertyChangedCallback(OnLazyItemsSourceChanged)));
public bool IsSourceAssigned
{
get;
private set;
} = false;
public IEnumerable LazyItemsSource
{
get => (IEnumerable)GetValue(LazyItemsSourceProperty);
set => SetValue(LazyItemsSourceProperty, value);
}
private static void OnLazyItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.Property == IsExpandedProperty)
{
if (!IsSourceAssigned)
{
SetValue(ItemsSourceProperty, new int[] { 1, 2, 3, 4, 5 });
IsSourceAssigned = true;
}
}
base.OnPropertyChanged(e);
}
private readonly static IEnumerable LazySource = new ArrayList(new[] { new object() });
public DynamicTreeViewItem()
{
Loaded += DynamicTreeViewItem_Loaded;
}
private void DynamicTreeViewItem_Loaded(object sender, RoutedEventArgs e)
{
SetValue(ItemsSourceProperty, LazySource);
}
}
public class DynamicHierarchicalDataTemplate : HierarchicalDataTemplate
{
public IEnumerable LazyItemsSource
{
get;
set;
}
}
}
, а также XAML:
<Window x:Class="WpfApp8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp8"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:DynamicHierarchicalDataTemplate DataType="{x:Type local:Node}" LazyItemsSource="{Binding Items}">
<TextBlock Text="{Binding}"/>
</local:DynamicHierarchicalDataTemplate>
</Window.Resources>
<Grid>
<local:DynamicTreeView ItemsSource="{Binding Tree.Items}"/>
</Grid>
</Window>
В настоящее время я могу использовать это решение, но я не понял, что вызывает жалобу компилятора:
<HierarchicalDataTemplate DataType="{x:Type local:Node}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type local:DynamicTreeViewItem}">
<Setter Property="LazyItemsSource" Value="{Binding Items}"/>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<TextBlock Text="{Binding}"/>
</HierarchicalDataTemplate>
У меня сложилось впечатление, что это должно быть довольно легко, но я не уверен, каковы внутренние детали DataTemplate.