Browse Source

Made TreeView work again and tests pass.

pull/9677/head
Steven Kirk 4 years ago
parent
commit
6f04196b84
  1. 32
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  2. 66
      src/Avalonia.Controls/ItemsControl.cs
  3. 8
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  4. 16
      src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs
  5. 438
      src/Avalonia.Controls/TreeView.cs
  6. 5
      src/Avalonia.Controls/TreeViewItem.cs
  7. 1
      src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml
  8. 1
      src/Avalonia.Themes.Simple/Controls/TreeViewItem.xaml
  9. 2866
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

32
src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
namespace Avalonia.Controls.Generators
{
public class TreeItemContainerGenerator : ItemContainerGenerator
{
internal TreeItemContainerGenerator(TreeView owner)
: base(owner)
{
Index = new TreeContainerIndex(owner);
}
public TreeContainerIndex Index { get; }
}
public class TreeContainerIndex
{
private readonly TreeView _owner;
internal TreeContainerIndex(TreeView owner) => _owner = owner;
[Obsolete("Use TreeView.GetRealizedTreeContainers")]
public IEnumerable<Control> Containers => _owner.GetRealizedTreeContainers();
[Obsolete("Use TreeView.TreeContainerFromItem")]
public Control? ContainerFromItem(object item) => _owner.TreeContainerFromItem(item);
[Obsolete("Use TreeView.TreeItemFromContainer")]
public object? ItemFromContainer(Control container) => _owner.TreeItemFromContainer(container);
}
}

66
src/Avalonia.Controls/ItemsControl.cs

@ -107,7 +107,12 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the <see cref="ItemContainerGenerator"/> for the control. /// Gets the <see cref="ItemContainerGenerator"/> for the control.
/// </summary> /// </summary>
public ItemContainerGenerator ItemContainerGenerator => _itemContainerGenerator ??= new(this); public ItemContainerGenerator ItemContainerGenerator
{
#pragma warning disable CS0612 // Type or member is obsolete
get => _itemContainerGenerator ??= CreateItemContainerGenerator();
#pragma warning restore CS0612 // Type or member is obsolete
}
/// <summary> /// <summary>
/// Gets or sets the items to display. /// Gets or sets the items to display.
@ -188,7 +193,8 @@ namespace Avalonia.Controls
/// </returns> /// </returns>
public Control? ContainerFromItem(object item) public Control? ContainerFromItem(object item)
{ {
throw new NotImplementedException(); var index = Items?.IndexOf(item) ?? -1;
return index >= 0 ? ContainerFromIndex(index) : null;
} }
/// <summary> /// <summary>
@ -210,8 +216,8 @@ namespace Avalonia.Controls
/// </returns> /// </returns>
public object? ItemFromContainer(Control container) public object? ItemFromContainer(Control container)
{ {
// TODO: Should this throw or return null of container isn't a container? var index = IndexFromContainer(container);
throw new NotImplementedException(); return index >= 0 && index < ItemCount ? Items!.ElementAt(index) : null;
} }
/// <summary> /// <summary>
@ -273,6 +279,8 @@ namespace Avalonia.Controls
if (container == item) if (container == item)
return; return;
var itemTemplate = GetEffectiveItemTemplate();
if (container is HeaderedContentControl hcc) if (container is HeaderedContentControl hcc)
{ {
hcc.Content = item; hcc.Content = item;
@ -282,20 +290,43 @@ namespace Avalonia.Controls
else if (item is not Visual) else if (item is not Visual)
hcc.Header = item; hcc.Header = item;
if (GetEffectiveItemTemplate() is { } it) if (itemTemplate is not null)
hcc.HeaderTemplate = it; hcc.HeaderTemplate = itemTemplate;
} }
else if (container is ContentControl cc) else if (container is ContentControl cc)
{ {
cc.Content = item; cc.Content = item;
if (GetEffectiveItemTemplate() is { } it) if (itemTemplate is not null)
cc.ContentTemplate = it; cc.ContentTemplate = itemTemplate;
} }
else if (container is ContentPresenter p) else if (container is ContentPresenter p)
{ {
p.Content = item; p.Content = item;
if (GetEffectiveItemTemplate() is { } it) if (itemTemplate is not null)
p.ContentTemplate = it; p.ContentTemplate = itemTemplate;
}
else if (container is ItemsControl ic)
{
if (itemTemplate is not null)
ic.ItemTemplate = itemTemplate;
if (ItemContainerTheme is { } ict)
ic.ItemContainerTheme = ict;
}
// This condition is separate because HeaderedItemsControl needs to also run the
// ItemsControl preparation.
if (container is HeaderedItemsControl hic)
{
hic.Header = item;
hic.HeaderTemplate = itemTemplate;
var treeTemplate = (itemTemplate ?? hic.FindDataTemplate(item)) as ITreeDataTemplate;
if (treeTemplate is not null)
{
if (item is not null && treeTemplate.ItemsSelector(item) is { } itemsBinding)
BindingOperations.Apply(hic, ItemsProperty, itemsBinding, null);
}
} }
} }
@ -316,6 +347,7 @@ namespace Avalonia.Controls
/// <param name="container">The container element.</param> /// <param name="container">The container element.</param>
protected internal virtual void ClearContainerForItemOverride(Control container) protected internal virtual void ClearContainerForItemOverride(Control container)
{ {
// TODO: Remove HeaderedItemsControl.Items binding.
} }
/// <summary> /// <summary>
@ -486,6 +518,20 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Creates the <see cref="ItemContainerGenerator"/>
/// </summary>
/// <remarks>
/// This method is only present for backwards compatibility with 0.10.x in order for
/// TreeView to be able to create a <see cref="TreeItemContainerGenerator"/>. Can be
/// removed in 12.0.
/// </remarks>
[Obsolete]
private protected virtual ItemContainerGenerator CreateItemContainerGenerator()
{
return new ItemContainerGenerator(this);
}
internal void AddLogicalChild(Control c) => LogicalChildren.Add(c); internal void AddLogicalChild(Control c) => LogicalChildren.Add(c);
internal void RemoveLogicalChild(Control c) => LogicalChildren.Remove(c); internal void RemoveLogicalChild(Control c) => LogicalChildren.Remove(c);

8
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using Avalonia.Input;
namespace Avalonia.Controls.Presenters namespace Avalonia.Controls.Presenters
{ {
@ -19,6 +20,13 @@ namespace Avalonia.Controls.Presenters
private PanelContainerGenerator? _generator; private PanelContainerGenerator? _generator;
static ItemsPresenter()
{
KeyboardNavigation.TabNavigationProperty.OverrideDefaultValue(
typeof(ItemsPresenter),
KeyboardNavigationMode.Once);
}
/// <summary> /// <summary>
/// Gets or sets a template which creates the <see cref="Panel"/> used to display the items. /// Gets or sets a template which creates the <see cref="Panel"/> used to display the items.
/// </summary> /// </summary>

16
src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs

@ -1,5 +1,6 @@
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
@ -15,6 +16,12 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<object?> HeaderProperty = public static readonly StyledProperty<object?> HeaderProperty =
HeaderedContentControl.HeaderProperty.AddOwner<HeaderedItemsControl>(); HeaderedContentControl.HeaderProperty.AddOwner<HeaderedItemsControl>();
/// <summary>
/// Defines the <see cref="HeaderTemplate"/> property.
/// </summary>
public static readonly StyledProperty<IDataTemplate?> HeaderTemplateProperty =
AvaloniaProperty.Register<HeaderedItemsControl, IDataTemplate?>(nameof(HeaderTemplate));
/// <summary> /// <summary>
/// Initializes static members of the <see cref="ContentControl"/> class. /// Initializes static members of the <see cref="ContentControl"/> class.
/// </summary> /// </summary>
@ -32,6 +39,15 @@ namespace Avalonia.Controls.Primitives
set { SetValue(HeaderProperty, value); } set { SetValue(HeaderProperty, value); }
} }
/// <summary>
/// Gets or sets the data template used to display the header content of the control.
/// </summary>
public IDataTemplate? HeaderTemplate
{
get => GetValue(HeaderTemplateProperty);
set => SetValue(HeaderTemplateProperty, value);
}
/// <summary> /// <summary>
/// Gets the header presenter from the control's template. /// Gets the header presenter from the control's template.
/// </summary> /// </summary>

438
src/Avalonia.Controls/TreeView.cs

@ -75,6 +75,12 @@ namespace Avalonia.Controls
remove => RemoveHandler(SelectingItemsControl.SelectionChangedEvent, value); remove => RemoveHandler(SelectingItemsControl.SelectionChangedEvent, value);
} }
/// <summary>
/// Gets the <see cref="TreeItemContainerGenerator"/> for the tree view.
/// </summary>
public new TreeItemContainerGenerator ItemContainerGenerator =>
(TreeItemContainerGenerator)base.ItemContainerGenerator;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to automatically scroll to newly selected items. /// Gets or sets a value indicating whether to automatically scroll to newly selected items.
/// </summary> /// </summary>
@ -182,8 +188,25 @@ namespace Avalonia.Controls
/// </remarks> /// </remarks>
public void SelectAll() public void SelectAll()
{ {
throw new NotImplementedException(); var allItems = new List<object>();
////SynchronizeItems(SelectedItems, ItemContainerGenerator.Index!.Items);
void AddItems(ItemsControl itemsControl)
{
if (itemsControl.Items is { } items)
{
foreach (var item in items)
allItems.Add(item);
}
foreach (var child in itemsControl.GetRealizedContainers())
{
if (child is ItemsControl childItemsControl)
AddItems(childItemsControl);
}
}
AddItems(this);
SynchronizeItems(SelectedItems, allItems);
} }
/// <summary> /// <summary>
@ -194,6 +217,62 @@ namespace Avalonia.Controls
SelectedItems.Clear(); SelectedItems.Clear();
} }
public IEnumerable<Control> GetRealizedTreeContainers()
{
static IEnumerable<Control> GetRealizedContainers(ItemsControl itemsControl)
{
foreach (var container in itemsControl.GetRealizedContainers())
{
yield return container;
if (container is ItemsControl itemsControlContainer)
foreach (var child in GetRealizedContainers(itemsControlContainer))
yield return child;
}
}
return GetRealizedContainers(this);
}
public Control? TreeContainerFromItem(object item)
{
static Control? TreeContainerFromItem(ItemsControl itemsControl, object item)
{
if (itemsControl.ContainerFromItem(item) is { } container)
return container;
foreach (var child in itemsControl.GetRealizedContainers())
{
if (child is ItemsControl childItemsControl &&
TreeContainerFromItem(childItemsControl, item) is { } childContainer)
return childContainer;
}
return null;
}
return TreeContainerFromItem(this, item);
}
public object? TreeItemFromContainer(Control container)
{
static object? TreeItemFromContainer(ItemsControl itemsControl, Control container)
{
if (itemsControl.ItemFromContainer(container) is { } item)
return item;
foreach (var child in itemsControl.GetRealizedContainers())
{
if (child is ItemsControl childItemsControl &&
TreeItemFromContainer(childItemsControl, container) is { } childContainer)
return childContainer;
}
return null;
}
return TreeItemFromContainer(this, container);
}
/// <summary> /// <summary>
/// Subscribes to the <see cref="SelectedItems"/> CollectionChanged event, if any. /// Subscribes to the <see cref="SelectedItems"/> CollectionChanged event, if any.
/// </summary> /// </summary>
@ -277,10 +356,9 @@ namespace Avalonia.Controls
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
foreach (var child in LogicalChildren) foreach (var container in GetRealizedTreeContainers())
{ {
if (child is Control container && IndexFromContainer(container) != -1) MarkContainerSelected(container, false);
MarkContainerSelected(container, false);
} }
if (SelectedItems.Count > 0) if (SelectedItems.Count > 0)
@ -333,7 +411,7 @@ namespace Avalonia.Controls
private void MarkItemSelected(object item, bool selected) private void MarkItemSelected(object item, bool selected)
{ {
var container = ContainerFromItem(item)!; var container = TreeContainerFromItem(item)!;
MarkContainerSelected(container, selected); MarkContainerSelected(container, selected);
} }
@ -366,6 +444,7 @@ namespace Avalonia.Controls
incc.CollectionChanged -= SelectedItemsCollectionChanged; incc.CollectionChanged -= SelectedItemsCollectionChanged;
} }
} }
(bool handled, IInputElement? next) ICustomKeyboardNavigation.GetNext(IInputElement element, (bool handled, IInputElement? next) ICustomKeyboardNavigation.GetNext(IInputElement element,
NavigationDirection direction) NavigationDirection direction)
{ {
@ -374,7 +453,7 @@ namespace Avalonia.Controls
if (!this.IsVisualAncestorOf((Visual)element)) if (!this.IsVisualAncestorOf((Visual)element))
{ {
var result = _selectedItem != null ? var result = _selectedItem != null ?
ContainerFromItem(_selectedItem) : TreeContainerFromItem(_selectedItem) :
ContainerFromIndex(0); ContainerFromIndex(0);
return (result != null, result); // SelectedItem may not be in the treeview. return (result != null, result); // SelectedItem may not be in the treeview.
@ -386,6 +465,9 @@ namespace Avalonia.Controls
return (false, null); return (false, null);
} }
protected internal override Control CreateContainerOverride() => new TreeViewItem();
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is TreeViewItem;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnGotFocus(GotFocusEventArgs e) protected override void OnGotFocus(GotFocusEventArgs e)
{ {
@ -441,54 +523,58 @@ namespace Avalonia.Controls
NavigationDirection direction, NavigationDirection direction,
bool intoChildren) bool intoChildren)
{ {
throw new NotImplementedException(); var parentItemsControl = from?.Parent switch
////IItemContainerGenerator? parentGenerator = GetParentContainerGenerator(from); {
TreeView tv => (ItemsControl)tv,
////if (parentGenerator == null) TreeViewItem i => i,
////{ _ => null
//// return null; };
////}
if (parentItemsControl == null)
////var index = from is not null ? parentGenerator.IndexFromContainer(from) : -1; {
////var parent = from?.Parent as ItemsControl; return null;
////TreeViewItem? result = null; }
////switch (direction) var index = from is not null ? parentItemsControl.IndexFromContainer(from) : -1;
////{ var parent = from?.Parent as ItemsControl;
//// case NavigationDirection.Up: TreeViewItem? result = null;
//// if (index > 0)
//// { switch (direction)
//// var previous = (TreeViewItem)parentGenerator.ContainerFromIndex(index - 1)!; {
//// result = previous.IsExpanded && previous.ItemCount > 0 ? case NavigationDirection.Up:
//// (TreeViewItem)previous.ItemContainerGenerator.ContainerFromIndex(previous.ItemCount - 1)! : if (index > 0)
//// previous; {
//// } var previous = (TreeViewItem)parentItemsControl.ContainerFromIndex(index - 1)!;
//// else result = previous.IsExpanded && previous.ItemCount > 0 ?
//// { (TreeViewItem)previous.ItemContainerGenerator.ContainerFromIndex(previous.ItemCount - 1)! :
//// result = from?.Parent as TreeViewItem; previous;
//// } }
else
//// break; {
result = from?.Parent as TreeViewItem;
//// case NavigationDirection.Down: }
//// case NavigationDirection.Right:
//// if (from?.IsExpanded == true && intoChildren && from.ItemCount > 0) break;
//// {
//// result = (TreeViewItem)from.ItemContainerGenerator.ContainerFromIndex(0)!; case NavigationDirection.Down:
//// } case NavigationDirection.Right:
//// else if (index < parent?.ItemCount - 1) if (from?.IsExpanded == true && intoChildren && from.ItemCount > 0)
//// { {
//// result = (TreeViewItem)parentGenerator.ContainerFromIndex(index + 1)!; result = (TreeViewItem)from.ItemContainerGenerator.ContainerFromIndex(0)!;
//// } }
//// else if (parent is TreeViewItem parentItem) else if (index < parent?.ItemCount - 1)
//// { {
//// return GetContainerInDirection(parentItem, direction, false); result = (TreeViewItem)parentItemsControl.ContainerFromIndex(index + 1)!;
//// } }
else if (parent is TreeViewItem parentItem)
//// break; {
////} return GetContainerInDirection(parentItem, direction, false);
}
////return result;
break;
}
return result;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -527,63 +613,68 @@ namespace Avalonia.Controls
bool toggleModifier = false, bool toggleModifier = false,
bool rightButton = false) bool rightButton = false)
{ {
throw new NotImplementedException(); var item = TreeItemFromContainer(container);
////var item = ItemContainerGenerator.Index!.ItemFromContainer(container);
if (item == null)
////if (item == null) {
////{ return;
//// return; }
////}
Control? selectedContainer = null;
////Control? selectedContainer = null;
if (SelectedItem != null)
////if (SelectedItem != null) {
////{ selectedContainer = TreeContainerFromItem(SelectedItem);
//// selectedContainer = ItemContainerGenerator.Index!.ContainerFromItem(SelectedItem); }
////}
var mode = SelectionMode;
////var mode = SelectionMode; var toggle = toggleModifier || mode.HasAllFlags(SelectionMode.Toggle);
////var toggle = toggleModifier || mode.HasAllFlags(SelectionMode.Toggle); var multi = mode.HasAllFlags(SelectionMode.Multiple);
////var multi = mode.HasAllFlags(SelectionMode.Multiple); var range = multi && rangeModifier && selectedContainer != null;
////var range = multi && rangeModifier && selectedContainer != null;
if (rightButton)
////if (rightButton) {
////{ if (!SelectedItems.Contains(item))
//// if (!SelectedItems.Contains(item)) {
//// { SelectSingleItem(item);
//// SelectSingleItem(item); }
//// } }
////} else if (!toggle && !range)
////else if (!toggle && !range) {
////{ SelectSingleItem(item);
//// SelectSingleItem(item); }
////} else if (multi && range)
////else if (multi && range) {
////{ SynchronizeItems(
//// SynchronizeItems( SelectedItems,
//// SelectedItems, GetItemsInRange(selectedContainer as TreeViewItem, container as TreeViewItem));
//// GetItemsInRange(selectedContainer as TreeViewItem, container as TreeViewItem)); }
////} else
////else {
////{ var i = SelectedItems.IndexOf(item);
//// var i = SelectedItems.IndexOf(item);
if (i != -1)
//// if (i != -1) {
//// { SelectedItems.Remove(item);
//// SelectedItems.Remove(item); }
//// } else
//// else {
//// { if (multi)
//// if (multi) {
//// { SelectedItems.Add(item);
//// SelectedItems.Add(item); }
//// } else
//// else {
//// { SelectedItem = item;
//// SelectedItem = item; }
//// } }
//// } }
////} }
[Obsolete]
private protected override ItemContainerGenerator CreateItemContainerGenerator()
{
return new TreeItemContainerGenerator(this);
} }
/// <summary> /// <summary>
@ -595,27 +686,24 @@ namespace Avalonia.Controls
/// <returns>Found first node.</returns> /// <returns>Found first node.</returns>
private static TreeViewItem? FindFirstNode(TreeView treeView, TreeViewItem nodeA, TreeViewItem nodeB) private static TreeViewItem? FindFirstNode(TreeView treeView, TreeViewItem nodeA, TreeViewItem nodeB)
{ {
return FindInContainers(treeView.ItemContainerGenerator, nodeA, nodeB); return FindInContainers(treeView, nodeA, nodeB);
} }
private static TreeViewItem? FindInContainers(ItemContainerGenerator containerGenerator, private static TreeViewItem? FindInContainers(ItemsControl itemsControl,
TreeViewItem nodeA, TreeViewItem nodeA,
TreeViewItem nodeB) TreeViewItem nodeB)
{ {
throw new NotImplementedException(); foreach (var container in itemsControl.GetRealizedContainers())
////IEnumerable<ItemContainerInfo> containers = containerGenerator.Containers; {
TreeViewItem? node = FindFirstNode(container as TreeViewItem, nodeA, nodeB);
////foreach (ItemContainerInfo container in containers)
////{
//// TreeViewItem? node = FindFirstNode(container.ContainerControl as TreeViewItem, nodeA, nodeB);
//// if (node != null) if (node != null)
//// { {
//// return node; return node;
//// } }
////} }
////return null; return null;
} }
private static TreeViewItem? FindFirstNode(TreeViewItem? node, TreeViewItem nodeA, TreeViewItem nodeB) private static TreeViewItem? FindFirstNode(TreeViewItem? node, TreeViewItem nodeA, TreeViewItem nodeB)
@ -632,7 +720,7 @@ namespace Avalonia.Controls
return match; return match;
} }
return FindInContainers(node.ItemContainerGenerator, nodeA, nodeB); return FindInContainers(node, nodeA, nodeB);
} }
/// <summary> /// <summary>
@ -643,60 +731,59 @@ namespace Avalonia.Controls
/// <param name="to">To container.</param> /// <param name="to">To container.</param>
private List<object> GetItemsInRange(TreeViewItem? from, TreeViewItem? to) private List<object> GetItemsInRange(TreeViewItem? from, TreeViewItem? to)
{ {
throw new NotImplementedException(); var items = new List<object>();
////var items = new List<object>();
////if (from == null || to == null) if (from == null || to == null)
////{ {
//// return items; return items;
////} }
////TreeViewItem? firstItem = FindFirstNode(this, from, to); TreeViewItem? firstItem = FindFirstNode(this, from, to);
////if (firstItem == null) if (firstItem == null)
////{ {
//// return items; return items;
////} }
////bool wasReversed = false; bool wasReversed = false;
////if (firstItem == to) if (firstItem == to)
////{ {
//// var temp = from; var temp = from;
//// from = to; from = to;
//// to = temp; to = temp;
//// wasReversed = true; wasReversed = true;
////} }
////TreeViewItem? node = from; TreeViewItem? node = from;
////while (node != to) while (node is not null && node != to)
////{ {
//// var item = ItemContainerGenerator.Index!.ItemFromContainer(node); var item = TreeItemFromContainer(node);
//// if (item != null) if (item != null)
//// { {
//// items.Add(item); items.Add(item);
//// } }
//// node = GetContainerInDirection(node, NavigationDirection.Down, true); node = GetContainerInDirection(node, NavigationDirection.Down, true);
////} }
////var toItem = ItemContainerGenerator.Index!.ItemFromContainer(to); var toItem = TreeItemFromContainer(to);
////if (toItem != null) if (toItem != null)
////{ {
//// items.Add(toItem); items.Add(toItem);
////} }
////if (wasReversed) if (wasReversed)
////{ {
//// items.Reverse(); items.Reverse();
////} }
////return items; return items;
} }
/// <summary> /// <summary>
@ -737,20 +824,11 @@ namespace Avalonia.Controls
/// <returns>The container or null if the event did not originate in a container.</returns> /// <returns>The container or null if the event did not originate in a container.</returns>
protected TreeViewItem? GetContainerFromEventSource(object eventSource) protected TreeViewItem? GetContainerFromEventSource(object eventSource)
{ {
throw new NotImplementedException(); var item = ((Visual)eventSource).GetSelfAndVisualAncestors()
////var item = ((Visual)eventSource).GetSelfAndVisualAncestors() .OfType<TreeViewItem>()
//// .OfType<TreeViewItem>() .FirstOrDefault();
//// .FirstOrDefault();
////if (item != null)
////{
//// if (item.ItemContainerGenerator.Index == ItemContainerGenerator.Index)
//// {
//// return item;
//// }
////}
////return null; return item?.TreeViewOwner == this ? item : null;
} }
/// <summary> /// <summary>
@ -801,7 +879,7 @@ namespace Avalonia.Controls
} }
else else
{ {
container.Classes.Set(":selected", selected); ((IPseudoClasses)container.Classes).Set(":selected", selected);
} }
} }

5
src/Avalonia.Controls/TreeViewItem.cs

@ -88,6 +88,11 @@ namespace Avalonia.Controls
private set { SetAndRaise(LevelProperty, ref _level, value); } private set { SetAndRaise(LevelProperty, ref _level, value); }
} }
internal TreeView? TreeViewOwner => _treeView;
protected internal override Control CreateContainerOverride() => new TreeViewItem();
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is TreeViewItem;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {

1
src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml

@ -88,6 +88,7 @@
Grid.Column="1" Grid.Column="1"
Focusable="False" Focusable="False"
Content="{TemplateBinding Header}" Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"
Margin="{TemplateBinding Padding}" /> Margin="{TemplateBinding Padding}" />

1
src/Avalonia.Themes.Simple/Controls/TreeViewItem.xaml

@ -58,6 +58,7 @@
Padding="{TemplateBinding Padding}" Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}" HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}"
Content="{TemplateBinding Header}" Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
Focusable="False" /> Focusable="False" />
</Grid> </Grid>
</Border> </Border>

2866
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

File diff suppressed because it is too large
Loading…
Cancel
Save