// 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.Linq; using Avalonia.Controls.Generators; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.LogicalTree; namespace Avalonia.Controls { /// /// An item in a . /// public class TreeViewItem : HeaderedItemsControl, ISelectable { /// /// Defines the property. /// public static readonly DirectProperty IsExpandedProperty = AvaloniaProperty.RegisterDirect( nameof(IsExpanded), o => o.IsExpanded, (o, v) => o.IsExpanded = v); /// /// Defines the property. /// public static readonly StyledProperty IsSelectedProperty = ListBoxItem.IsSelectedProperty.AddOwner(); /// /// Defines the property. /// public static readonly DirectProperty LevelProperty = AvaloniaProperty.RegisterDirect( nameof(Level), o => o.Level); private static readonly ITemplate DefaultPanel = new FuncTemplate(() => new StackPanel()); private TreeView _treeView; private bool _isExpanded; private int _level; /// /// Initializes static members of the class. /// static TreeViewItem() { SelectableMixin.Attach(IsSelectedProperty); FocusableProperty.OverrideDefaultValue(true); ItemsPanelProperty.OverrideDefaultValue(DefaultPanel); } /// /// Gets or sets a value indicating whether the item is expanded to show its children. /// public bool IsExpanded { get { return _isExpanded; } set { SetAndRaise(IsExpandedProperty, ref _isExpanded, value); } } /// /// Gets or sets the selection state of the item. /// public bool IsSelected { get { return GetValue(IsSelectedProperty); } set { SetValue(IsSelectedProperty, value); } } /// /// Gets the level/indentation of the item. /// public int Level { get { return _level; } private set { SetAndRaise(LevelProperty, ref _level, value); } } /// /// Gets the for the tree view. /// public new ITreeItemContainerGenerator ItemContainerGenerator => (ITreeItemContainerGenerator)base.ItemContainerGenerator; /// protected override IItemContainerGenerator CreateItemContainerGenerator() { return new TreeItemContainerGenerator( this, TreeViewItem.HeaderProperty, TreeViewItem.ItemTemplateProperty, TreeViewItem.ItemsProperty, TreeViewItem.IsExpandedProperty, _treeView?.ItemContainerGenerator.Index ?? new TreeContainerIndex()); } /// protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { base.OnAttachedToLogicalTree(e); _treeView = this.GetLogicalAncestors().OfType().FirstOrDefault(); Level = CalculateDistanceFromLogicalParent(this) - 1; if (ItemTemplate == null && _treeView?.ItemTemplate != null) { ItemTemplate = _treeView.ItemTemplate; } } protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) { base.OnDetachedFromLogicalTree(e); ItemContainerGenerator.Clear(); } /// protected override void OnKeyDown(KeyEventArgs e) { if (!e.Handled) { switch (e.Key) { case Key.Right: if (Items != null && Items.Cast().Any()) { IsExpanded = true; } e.Handled = true; break; case Key.Left: IsExpanded = false; e.Handled = true; break; } } // Don't call base.OnKeyDown - let events bubble up to containing TreeView. } private static int CalculateDistanceFromLogicalParent(ILogical logical, int @default = -1) where T : class { var result = 0; while (logical != null && logical.GetType() != typeof(T)) { ++result; logical = logical.LogicalParent; } return logical != null ? result : @default; } } }