Browse Source

Merge pull request #10454 from MrJul/itemspanel-no-logical-children

Don't add ItemsControl items to its panel logical children
pull/10530/head
Steven Kirk 4 years ago
committed by GitHub
parent
commit
3c1177ced5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 35
      src/Avalonia.Controls/Panel.cs
  2. 1
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  3. 15
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  4. 15
      tests/Avalonia.Controls.UnitTests/PanelTests.cs
  5. 2
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  6. 2
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

35
src/Avalonia.Controls/Panel.cs

@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Reactive;
using Avalonia.Styling;
namespace Avalonia.Controls
{
@ -59,6 +58,11 @@ namespace Avalonia.Controls
set { SetValue(BackgroundProperty, value); }
}
/// <summary>
/// Gets whether the <see cref="Panel"/> hosts the items created by an <see cref="ItemsPresenter"/>.
/// </summary>
public bool IsItemsHost { get; internal set; }
event EventHandler<ChildIndexChangedEventArgs>? IChildIndexProvider.ChildIndexChanged
{
add
@ -129,24 +133,29 @@ namespace Avalonia.Controls
/// <param name="e">The event args.</param>
protected virtual void ChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
List<Control> controls;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
controls = e.NewItems!.OfType<Control>().ToList();
LogicalChildren.InsertRange(e.NewStartingIndex, controls);
if (!IsItemsHost)
{
LogicalChildren.InsertRange(e.NewStartingIndex, e.NewItems!.OfType<Control>().ToList());
}
VisualChildren.InsertRange(e.NewStartingIndex, e.NewItems!.OfType<Visual>());
break;
case NotifyCollectionChangedAction.Move:
LogicalChildren.MoveRange(e.OldStartingIndex, e.OldItems!.Count, e.NewStartingIndex);
VisualChildren.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
if (!IsItemsHost)
{
LogicalChildren.MoveRange(e.OldStartingIndex, e.OldItems!.Count, e.NewStartingIndex);
}
VisualChildren.MoveRange(e.OldStartingIndex, e.OldItems!.Count, e.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Remove:
controls = e.OldItems!.OfType<Control>().ToList();
LogicalChildren.RemoveAll(controls);
if (!IsItemsHost)
{
LogicalChildren.RemoveAll(e.OldItems!.OfType<Control>().ToList());
}
VisualChildren.RemoveAll(e.OldItems!.OfType<Visual>());
break;
@ -155,7 +164,10 @@ namespace Avalonia.Controls
{
var index = i + e.OldStartingIndex;
var child = (Control)e.NewItems![i]!;
LogicalChildren[index] = child;
if (!IsItemsHost)
{
LogicalChildren[index] = child;
}
VisualChildren[index] = child;
}
break;
@ -200,6 +212,7 @@ namespace Avalonia.Controls
return child is Control control ? Children.IndexOf(control) : -1;
}
/// <inheritdoc />
public bool TryGetTotalCount(out int count)
{
count = Children.Count;

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

@ -167,6 +167,7 @@ namespace Avalonia.Controls.Presenters
Panel = ItemsPanel.Build();
Panel.SetValue(TemplatedParentProperty, TemplatedParent);
Panel.IsItemsHost = true;
_scrollSnapPointsInfo = Panel as IScrollSnapPointsInfo;
LogicalChildren.Add(Panel);
VisualChildren.Add(Panel);

15
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@ -73,6 +73,19 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(target, target.Presenter.Panel.TemplatedParent);
}
[Fact]
public void Panel_Should_Have_ItemsHost_Set_To_True()
{
var target = new ItemsControl();
target.Template = GetTemplate();
target.Items = new[] { "Foo" };
target.ApplyTemplate();
target.Presenter!.ApplyTemplate();
Assert.True(target.Presenter.Panel!.IsItemsHost);
}
[Fact]
public void Container_Should_Have_TemplatedParent_Set_To_Null()
{
@ -634,7 +647,7 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
var item = target.Presenter.Panel.LogicalChildren[0];
var item = target.LogicalChildren[0];
Assert.Null(NameScope.GetNameScope((TextBlock)item));
}

15
tests/Avalonia.Controls.UnitTests/PanelTests.cs

@ -141,5 +141,20 @@ namespace Avalonia.Controls.UnitTests
var panel = new Panel();
Assert.Throws<ArgumentNullException>(() => panel.Children.Add(null!));
}
[Fact]
public void Adding_Control_To_Items_Host_Panel_Should_Not_Affect_Logical_Children()
{
var child = new Control();
var realParent = new ContentControl { Content = child };
var panel = new Panel { IsItemsHost = true };
panel.Children.Add(child);
Assert.Empty(panel.LogicalChildren);
Assert.Same(child.Parent, realParent);
Assert.Same(child.GetLogicalParent(), realParent);
Assert.Same(child.GetVisualParent(), panel);
}
}
}

2
tests/Avalonia.Controls.UnitTests/TabControlTests.cs

@ -327,7 +327,7 @@ namespace Avalonia.Controls.UnitTests
ApplyTemplate(target);
var logicalChildren = target.ItemsPresenterPart.Panel.GetLogicalChildren();
var logicalChildren = target.GetLogicalChildren();
var result = logicalChildren
.OfType<TabItem>()

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

@ -1178,7 +1178,7 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
var item = target.Presenter.Panel.LogicalChildren[0];
var item = target.LogicalChildren[0];
Assert.Null(NameScope.GetNameScope((TreeViewItem)item));
}

Loading…
Cancel
Save