Я хотел бы выполнить отложенную загрузку 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.
Это определенно хороший аргумент @Clemens. Я скопировал это из исходного кода и не проверял. Другой момент с IsitemsItsOwnContainer верен, но в моем случае, возможно, в этом нет необходимости. Исходный код из TreeView делает следующее: возвращаемый элемент - TreeViewItem. Поскольку мой элемент является производным от TreeViewItem, это должно быть проблемой. Тем не менее, он не работает и по-прежнему показывает ту же ошибку.
@msedi, LazyItemsSource не является DP, поэтому он не поддерживает привязку LazyItemsSource = "{Binding Items}"
@Ash: Я так понимаю, ты так думаешь. В реализации нового DynamicTreeViewItem это DependencyProperty, если вы проверите код. В HierarchicalDataTemplate, если вы проверяете исходный источник, даже ItemsSource является не DependencyProperty, а простым свойством.
если вы проверите исходный источник, ItemsSource имеет тип BindingBase
, который совместим с Binding
Ах. Да, вы правы. Я уже изменил это на то, как он используется в HierarchicalDataTemplate, и сделал LazyItemsSource также BindingBase. Но он по-прежнему не работает и дает ту же ошибку.
typeof(ItemsControl)
как третий параметр DependencyProperty.Register определенно неверен. Это должен бытьtypeof(DynamicTreeViewItem)
. Кроме того, когда вы переопределяетеGetContainerForItemOverride
, вы также должны переопределятьIsItemItsOwnContainer
.