// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Collections.Generic; using System.Linq; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.LogicalTree; namespace Avalonia.Controls.Generators { /// /// Creates containers for tree items and maintains a list of created containers. /// /// The type of the container. public class TreeItemContainerGenerator : ItemContainerGenerator, ITreeItemContainerGenerator where T : class, IControl, new() { private TreeView _treeView; /// /// Initializes a new instance of the class. /// /// The owner control. /// The container's Content property. /// The container's ContentTemplate property. /// The container's Items property. /// The container's IsExpanded property. public TreeItemContainerGenerator( IControl owner, AvaloniaProperty contentProperty, AvaloniaProperty contentTemplateProperty, AvaloniaProperty itemsProperty, AvaloniaProperty isExpandedProperty) : base(owner, contentProperty, contentTemplateProperty) { Contract.Requires(owner != null); Contract.Requires(contentProperty != null); Contract.Requires(itemsProperty != null); Contract.Requires(isExpandedProperty != null); ItemsProperty = itemsProperty; IsExpandedProperty = isExpandedProperty; UpdateIndex(); } /// /// Gets the container index for the tree. /// public TreeContainerIndex Index { get; private set; } /// /// Gets the item container's Items property. /// protected AvaloniaProperty ItemsProperty { get; } /// /// Gets the item container's IsExpanded property. /// protected AvaloniaProperty IsExpandedProperty { get; } /// protected override IControl CreateContainer(object item) { var container = item as T; if (item == null) { return null; } else if (container != null) { Index?.Add(item, container); return container; } else { var template = GetTreeDataTemplate(item, ItemTemplate); var result = new T(); result.SetValue(ContentProperty, template.Build(item), BindingPriority.Style); var itemsSelector = template.ItemsSelector(item); if (itemsSelector != null) { BindingOperations.Apply(result, ItemsProperty, itemsSelector, null); } if (!(item is IControl)) { result.DataContext = item; } Index?.Add(item, result); return result; } } public override IEnumerable Clear() { var items = base.Clear(); Index?.Remove(0, items); return items; } public override IEnumerable Dematerialize(int startingIndex, int count) { Index?.Remove(startingIndex, GetContainerRange(startingIndex, count)); return base.Dematerialize(startingIndex, count); } public override IEnumerable RemoveRange(int startingIndex, int count) { Index?.Remove(startingIndex, GetContainerRange(startingIndex, count)); return base.RemoveRange(startingIndex, count); } public override bool TryRecycle(int oldIndex, int newIndex, object item) => false; public void UpdateIndex() { if (Owner is TreeView treeViewOwner && Index == null) { Index = new TreeContainerIndex(); _treeView = treeViewOwner; } else { var treeView = Owner.GetSelfAndLogicalAncestors().OfType().FirstOrDefault(); if (treeView != _treeView) { Clear(); Index = treeView?.ItemContainerGenerator?.Index; _treeView = treeView; } } } class WrapperTreeDataTemplate : ITreeDataTemplate { private readonly IDataTemplate _inner; public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner; public IControl Build(object param) => _inner.Build(param); public bool SupportsRecycling => _inner.SupportsRecycling; public bool Match(object data) => _inner.Match(data); public InstancedBinding ItemsSelector(object item) => null; } private ITreeDataTemplate GetTreeDataTemplate(object item, IDataTemplate primary) { var template = Owner.FindDataTemplate(item, primary) ?? FuncDataTemplate.Default; var treeTemplate = template as ITreeDataTemplate ?? new WrapperTreeDataTemplate(template); return treeTemplate; } } }