Browse Source

Implement GetTemplateDescendants (#21245)

* Implement GetTemplateDescendants

* Use GetTemplateDescendants in Avalonia.Controls

* Use GetTemplateDescendants in tests

* Fix obsolete warnings
pull/21275/head
Julien Lebosquain 5 months ago
committed by GitHub
parent
commit
5e2f4ccd49
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs
  2. 8
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  3. 37
      src/Avalonia.Controls/Templates/TemplateExtensions.cs
  4. 4
      tests/Avalonia.Base.UnitTests/Layout/FullLayoutTests.cs
  5. 2
      tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
  6. 2
      tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs
  7. 2
      tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
  8. 2
      tests/Avalonia.Controls.UnitTests/CommandBarTests.cs
  9. 2
      tests/Avalonia.Controls.UnitTests/ContentControlTests.cs
  10. 4
      tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs
  11. 6
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  12. 10
      tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs
  13. 8
      tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs
  14. 4
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
  15. 6
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  16. 2
      tests/Avalonia.Controls.UnitTests/Templates/TemplateExtensionsTests.cs
  17. 2
      tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs
  18. 2
      tests/Avalonia.LeakTests/ControlTests.cs
  19. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs
  20. 8
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

2
src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs

@ -34,7 +34,7 @@ namespace Avalonia.Controls.Primitives
if (visual is TopLevel topLevel)
{
manager = topLevel.GetTemplateChildren()
manager = topLevel.GetTemplateDescendants()
.OfType<VisualLayerManager>()
.FirstOrDefault();
}

8
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -317,7 +317,7 @@ namespace Avalonia.Controls.Primitives
{
if (VisualChildren.Count > 0)
{
foreach (var child in this.GetTemplateChildren())
foreach (var child in this.GetTemplateDescendants())
{
child.TemplatedParent = null;
((ISetLogicalParent)child).SetParent(null);
@ -350,11 +350,11 @@ namespace Avalonia.Controls.Primitives
/// <inheritdoc/>
protected override Control GetTemplateFocusTarget()
{
foreach (Control child in this.GetTemplateChildren())
foreach (var child in this.GetTemplateDescendants())
{
if (GetIsTemplateFocusTarget(child))
if (child is Control control && GetIsTemplateFocusTarget(control))
{
return child;
return control;
}
}

37
src/Avalonia.Controls/Templates/TemplateExtensions.cs

@ -1,26 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Styling;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Templates
{
/// <summary>
/// Contains extension methods for <see cref="TemplatedControl"/>.
/// </summary>
public static class TemplateExtensions
{
/// <summary>
/// Gets the list of all control descendants that are part of the template of a <see cref="TemplatedControl"/>,
/// i.e. their <see cref="StyledElement.TemplatedParent"/> is <paramref name="control"/>.
/// </summary>
/// <param name="control">The control whose descendants will be returned.</param>
/// <returns>An enumeration of <see cref="Control"/> objects.</returns>
[Obsolete($"Use {nameof(GetTemplateDescendants)}")]
public static IEnumerable<Control> GetTemplateChildren(this TemplatedControl control)
{
foreach (Control child in GetTemplateChildren(control, control))
foreach (var child in control.GetTemplateDescendants())
{
yield return child;
if (child is Control childControl)
{
yield return childControl;
}
}
}
private static IEnumerable<Control> GetTemplateChildren(Control control, TemplatedControl templatedParent)
/// <summary>
/// Gets the list of all visual descendants that are part of the template of a <see cref="TemplatedControl"/>,
/// i.e. their <see cref="StyledElement.TemplatedParent"/> is <paramref name="control"/>.
/// </summary>
/// <param name="control">The control whose descendants will be returned.</param>
/// <returns>An enumeration of <see cref="Visual"/> objects.</returns>
public static IEnumerable<Visual> GetTemplateDescendants(this TemplatedControl control)
{
return GetTemplateDescendants(control, control);
}
private static IEnumerable<Visual> GetTemplateDescendants(Visual control, TemplatedControl templatedParent)
{
foreach (Control child in control.GetVisualChildren())
foreach (var child in control.GetVisualChildren())
{
var childTemplatedParent = child.TemplatedParent;
@ -31,7 +52,7 @@ namespace Avalonia.Controls.Templates
if (childTemplatedParent != null)
{
foreach (var descendant in GetTemplateChildren(child, templatedParent))
foreach (var descendant in GetTemplateDescendants(child, templatedParent))
{
yield return descendant;
}

4
tests/Avalonia.Base.UnitTests/Layout/FullLayoutTests.cs

@ -86,8 +86,8 @@ namespace Avalonia.Base.UnitTests.Layout
Assert.Equal(new Point(300, 200), Position(scrollViewer));
Assert.Equal(new Size(400, 400), textBlock.Bounds.Size);
var scrollBars = scrollViewer.GetTemplateChildren().OfType<ScrollBar>().ToList();
var presenters = scrollViewer.GetTemplateChildren().OfType<ScrollContentPresenter>().ToList();
var scrollBars = scrollViewer.GetTemplateDescendants().OfType<ScrollBar>().ToList();
var presenters = scrollViewer.GetTemplateDescendants().OfType<ScrollContentPresenter>().ToList();
Assert.Equal(2, scrollBars.Count);
Assert.Single(presenters);

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

@ -1235,7 +1235,7 @@ namespace Avalonia.Controls.UnitTests
}
private TextBox GetTextBox(AutoCompleteBox control)
{
return control.GetTemplateChildren()
return control.GetTemplateDescendants()
.OfType<TextBox>()
.First();
}

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

@ -155,7 +155,7 @@ namespace Avalonia.Controls.UnitTests
private TextBox GetTextBox(CalendarDatePicker control)
{
return control.GetTemplateChildren()
return control.GetTemplateDescendants()
.OfType<TextBox>()
.First();
}

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

@ -834,7 +834,7 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter!.ApplyTemplate();
var containerPanel = target.GetTemplateChildren().OfType<Panel>().FirstOrDefault(x => x.Name == "container");
var containerPanel = target.GetTemplateDescendants().OfType<Panel>().FirstOrDefault(x => x.Name == "container");
var editableTextBox = containerPanel?.GetVisualDescendants().OfType<TextBox>().FirstOrDefault(x => x.Name == "PART_EditableTextBox");
var popup = containerPanel?.GetVisualDescendants().OfType<Popup>().FirstOrDefault(x => x.Name == "PART_Popup");
var popupScrollViewer = popup?.Child as ScrollViewer;

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

@ -495,7 +495,7 @@ public class CommandBarDefaultsTests : ScopedTestBase
command.ApplyStyling();
command.ApplyTemplate();
var presenter = command.GetTemplateChildren()
var presenter = command.GetTemplateDescendants()
.OfType<ContentPresenter>()
.Single(x => x.Name == "PART_IconPresenter");

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

@ -57,7 +57,7 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter!.ApplyTemplate();
foreach (Control child in target.GetTemplateChildren())
foreach (var child in target.GetTemplateDescendants().OfType<Control>())
Assert.Equal("foo", child.Tag);
}

4
tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs

@ -142,7 +142,7 @@ namespace Avalonia.Controls.UnitTests
}
private static TextBox GetTextBox(NumericUpDown control)
{
return control.GetTemplateChildren()
return control.GetTemplateDescendants()
.OfType<ButtonSpinner>()
.Select(b => b.Content)
.OfType<TextBox>()
@ -151,7 +151,7 @@ namespace Avalonia.Controls.UnitTests
private static ButtonSpinner GetSpinner(NumericUpDown control)
{
return control.GetTemplateChildren()
return control.GetTemplateDescendants()
.OfType<ButtonSpinner>()
.First();
}

6
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -329,7 +329,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate();
var popup = (Popup)target.GetTemplateChildren().First(x => x.Name == "popup");
var popup = (Popup)target.GetTemplateDescendants().First(x => x.Name == "popup");
popup.Open();
var popupRoot = (Control)popup.Host!;
@ -420,7 +420,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate();
var popup = (Popup)target.GetTemplateChildren().First(x => x.Name == "popup");
var popup = (Popup)target.GetTemplateDescendants().First(x => x.Name == "popup");
popup.Open();
var popupRoot = (Control)popup.Host!;
@ -515,7 +515,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate();
var popup = (Popup)target.GetTemplateChildren().First(x => x.Name == "popup");
var popup = (Popup)target.GetTemplateDescendants().First(x => x.Name == "popup");
popup.Open();
var popupRoot = (Control)popup.Host!;

10
tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs

@ -20,7 +20,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
var track = (Track)target.GetTemplateDescendants().First(x => x.Name == "track");
target.Value = 50;
Assert.Equal(50, track.Value);
@ -35,7 +35,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
var track = (Track)target.GetTemplateDescendants().First(x => x.Name == "track");
track.Value = 50;
Assert.Equal(50, target.Value);
@ -51,7 +51,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate();
var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
var track = (Track)target.GetTemplateDescendants().First(x => x.Name == "track");
target.Value = 25;
track.Value = 50;
@ -68,7 +68,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate();
var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
var track = (Track)target.GetTemplateDescendants().First(x => x.Name == "track");
var raisedEvent = Assert.Raises<ScrollEventArgs>(
handler => target.Scroll += handler,
@ -97,7 +97,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate();
var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
var track = (Track)target.GetTemplateDescendants().First(x => x.Name == "track");
var raisedEvent = Assert.Raises<ScrollEventArgs>(
handler => target.Scroll += handler,

8
tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs

@ -204,7 +204,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate();
foreach (Control child in target.GetTemplateChildren())
foreach (var child in target.GetTemplateDescendants().OfType<Control>())
Assert.Equal("foo", child.Tag);
}
@ -237,11 +237,11 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate();
var contentControl = target.GetTemplateChildren().OfType<ContentControl>().Single();
var contentControl = target.GetTemplateDescendants().OfType<ContentControl>().Single();
contentControl.ApplyTemplate();
var border = contentControl.GetTemplateChildren().OfType<Border>().Single();
var presenter = contentControl.GetTemplateChildren().OfType<ContentPresenter>().Single();
var border = contentControl.GetTemplateDescendants().OfType<Border>().Single();
var presenter = contentControl.GetTemplateDescendants().OfType<ContentPresenter>().Single();
var decorator = (Decorator)presenter.Content!;
var textBlock = (TextBlock)decorator.Child!;

4
tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

@ -610,9 +610,9 @@ namespace Avalonia.Controls.UnitTests
private Thumb GetVerticalThumb(ScrollViewer target)
{
var scrollbar = Assert.IsType<ScrollBar>(
target.GetTemplateChildren().FirstOrDefault(x => x.Name == "PART_VerticalScrollBar"));
target.GetTemplateDescendants().FirstOrDefault(x => x.Name == "PART_VerticalScrollBar"));
var track = Assert.IsType<Track>(
scrollbar.GetTemplateChildren().FirstOrDefault(x => x.Name == "track"));
scrollbar.GetTemplateDescendants().FirstOrDefault(x => x.Name == "track"));
return Assert.IsType<Thumb>(track.Thumb);
}

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

@ -1701,7 +1701,7 @@ namespace Avalonia.Controls.UnitTests
tabItem.ApplyTemplate();
tabItem.Presenter!.UpdateChild();
var iconPresenter = tabItem.GetTemplateChildren().OfType<ContentPresenter>().First(x => x.Name == "PART_IconPresenter");
var iconPresenter = tabItem.GetTemplateDescendants().OfType<ContentPresenter>().First(x => x.Name == "PART_IconPresenter");
Assert.NotNull(iconPresenter);
Assert.Equal("home", iconPresenter!.Content);
Assert.NotNull(iconPresenter.ContentTemplate);
@ -1729,7 +1729,7 @@ namespace Avalonia.Controls.UnitTests
tabItem.ApplyTemplate();
tabItem.Presenter!.UpdateChild();
var iconPresenter = tabItem.GetTemplateChildren().OfType<ContentPresenter>().First(x => x.Name == "PART_IconPresenter");
var iconPresenter = tabItem.GetTemplateDescendants().OfType<ContentPresenter>().First(x => x.Name == "PART_IconPresenter");
Assert.NotNull(iconPresenter);
Assert.Same(icon, iconPresenter!.Content);
Assert.Null(iconPresenter.ContentTemplate);
@ -1748,7 +1748,7 @@ namespace Avalonia.Controls.UnitTests
tabItem.ApplyTemplate();
tabItem.Presenter!.UpdateChild();
var iconPresenter = tabItem.GetTemplateChildren().OfType<ContentPresenter>().First(x => x.Name == "PART_IconPresenter");
var iconPresenter = tabItem.GetTemplateDescendants().OfType<ContentPresenter>().First(x => x.Name == "PART_IconPresenter");
Assert.Equal("first", iconPresenter!.Content);
tabItem.Icon = "second";

2
tests/Avalonia.Controls.UnitTests/Templates/TemplateExtensionsTests.cs

@ -38,7 +38,7 @@ namespace Avalonia.Controls.Templates.UnitTests
border3.Child = border4;
border4.Child = border5;
var result = target.GetTemplateChildren().Select(x => x.Name).ToArray();
var result = target.GetTemplateDescendants().Select(x => x.Name).ToArray();
Assert.Equal(new[] { "border1", "inner", "border4" }, result);
}

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

@ -371,7 +371,7 @@ namespace Avalonia.Controls.UnitTests
private static ContentPresenter GetContentPresenters2(TransitioningContentControl target)
{
return Assert.IsType<ContentPresenter>(target
.GetTemplateChildren()
.GetTemplateDescendants()
.First(x => x.Name == "PART_ContentPresenter2"));
}

2
tests/Avalonia.LeakTests/ControlTests.cs

@ -251,7 +251,7 @@ namespace Avalonia.LeakTests
Assert.Same(textBox, window.Presenter!.Child);
// Get the border from the TextBox template.
var border = textBox.GetTemplateChildren().FirstOrDefault(x => x.Name == "border");
var border = textBox.GetTemplateDescendants().FirstOrDefault(x => x.Name == "border");
// The TextBox should have subscriptions to its Classes collection from the
// default theme.

2
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs

@ -67,7 +67,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
window.ApplyTemplate();
button.ApplyTemplate();
Assert.Equal(button.Tag, button.GetTemplateChildren().OfType<Grid>().First().ColumnDefinitions[0].Width);
Assert.Equal(button.Tag, button.GetTemplateDescendants().OfType<Grid>().First().ColumnDefinitions[0].Width);
}
}
}

8
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -1011,7 +1011,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
contentControl.DataContext = new TestDataContext(); // should be ignored
contentControl.Measure(new Size(10, 10));
var result = contentControl.GetTemplateChildren().OfType<ContentPresenter>().First();
var result = contentControl.GetTemplateDescendants().OfType<ContentPresenter>().First();
Assert.Equal(false, result.Focusable);
}
}
@ -1046,7 +1046,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
textBox.DataContext = new TestDataContext(); // should be ignored
textBox.Measure(new Size(10, 10));
var result = textBox.GetTemplateChildren().OfType<ContentPresenter>().First();
var result = textBox.GetTemplateDescendants().OfType<ContentPresenter>().First();
Assert.Equal(textBox.InnerLeftContent, result.Content);
}
}
@ -1080,7 +1080,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
window.ApplyTemplate();
button.ApplyTemplate();
Assert.Equal(button.Tag, button.GetTemplateChildren().OfType<Grid>().First().ColumnDefinitions[0].Width);
Assert.Equal(button.Tag, button.GetTemplateDescendants().OfType<Grid>().First().ColumnDefinitions[0].Width);
}
}
@ -1109,7 +1109,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
var contentControl = AvaloniaRuntimeXamlLoader.Parse<ContentControl>(xaml);
contentControl.Measure(new Size(10, 10));
var result = contentControl.GetTemplateChildren().OfType<ContentPresenter>().First();
var result = contentControl.GetTemplateDescendants().OfType<ContentPresenter>().First();
Assert.Equal("Hello", result.Content);
}

Loading…
Cancel
Save