Browse Source

Merge pull request #8474 from AvaloniaUI/fixes/menu-templated-parent

Fix overwriting TemplatedParent when opening a Popup.
pull/8454/head
Max Katz 4 years ago
committed by GitHub
parent
commit
fc4101c162
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  2. 44
      tests/Avalonia.Controls.UnitTests/MenuItemTests.cs
  3. 147
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

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

@ -387,6 +387,7 @@ namespace Avalonia.Controls.Primitives
/// Sets the TemplatedParent property for the created template children.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="templatedParent">The templated parent to apply.</param>
internal static void ApplyTemplatedParent(IStyledElement control, ITemplatedControl? templatedParent)
{
control.SetValue(TemplatedParentProperty, templatedParent);
@ -396,7 +397,7 @@ namespace Avalonia.Controls.Primitives
for (var i = 0; i < count; i++)
{
if (children[i] is IStyledElement child)
if (children[i] is IStyledElement child && child.TemplatedParent is null)
{
ApplyTemplatedParent(child, templatedParent);
}

44
tests/Avalonia.Controls.UnitTests/MenuItemTests.cs

@ -8,6 +8,7 @@ using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Moq;
using Xunit;
@ -301,6 +302,49 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(3, canExecuteCallCount);
}
}
[Fact]
public void TemplatedParent_Should_Not_Be_Applied_To_Submenus()
{
using (Application())
{
MenuItem topLevelMenu;
MenuItem childMenu1;
MenuItem childMenu2;
var menu = new Menu
{
Items = new[]
{
(topLevelMenu = new MenuItem
{
Header = "Foo",
Items = new[]
{
(childMenu1 = new MenuItem { Header = "Bar" }),
(childMenu2 = new MenuItem { Header = "Baz" }),
}
}),
}
};
var window = new Window { Content = menu };
window.LayoutManager.ExecuteInitialLayoutPass();
topLevelMenu.IsSubMenuOpen = true;
Assert.True(((IVisual)childMenu1).IsAttachedToVisualTree);
Assert.Null(childMenu1.TemplatedParent);
Assert.Null(childMenu2.TemplatedParent);
topLevelMenu.IsSubMenuOpen = false;
topLevelMenu.IsSubMenuOpen = true;
Assert.Null(childMenu1.TemplatedParent);
Assert.Null(childMenu2.TemplatedParent);
}
}
private IDisposable Application()
{
var screen = new PixelRect(new PixelPoint(), new PixelSize(100, 100));

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

@ -295,7 +295,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
[Fact]
public void Templated_Control_With_Popup_In_Template_Should_Set_TemplatedParent()
public void ContentControl_With_Popup_In_Template_Should_Set_TemplatedParent()
{
// Test uses OverlayPopupHost default template
using (CreateServices())
@ -384,6 +384,134 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
}
[Fact]
public void ItemsControl_With_Popup_In_Template_Should_Set_TemplatedParent()
{
// Test uses OverlayPopupHost default template
using (CreateServices())
{
PopupItemsControl target;
var item = new Border();
var root = PreparedWindow(target = new PopupItemsControl
{
Items = new[] { item },
Template = new FuncControlTemplate<PopupItemsControl>(PopupItemsControlTemplate),
}); ;
root.Show();
target.ApplyTemplate();
var popup = (Popup)target.GetTemplateChildren().First(x => x.Name == "popup");
popup.Open();
var popupRoot = (Control)popup.Host;
popupRoot.Measure(Size.Infinity);
popupRoot.Arrange(new Rect(popupRoot.DesiredSize));
var children = popupRoot.GetVisualDescendants().ToList();
var types = children.Select(x => x.GetType().Name).ToList();
if (UsePopupHost)
{
Assert.Equal(
new[]
{
"LayoutTransformControl",
"VisualLayerManager",
"ContentPresenter",
"ItemsPresenter",
"StackPanel",
"Border",
},
types);
}
else
{
Assert.Equal(
new[]
{
"LayoutTransformControl",
"Panel",
"Border",
"VisualLayerManager",
"ContentPresenter",
"ItemsPresenter",
"StackPanel",
"Border",
},
types);
}
var templatedParents = children
.OfType<IControl>()
.Select(x => x.TemplatedParent).ToList();
if (UsePopupHost)
{
Assert.Equal(
new object[]
{
popupRoot,
popupRoot,
popupRoot,
target,
target,
null,
},
templatedParents);
}
else
{
Assert.Equal(
new object[]
{
popupRoot,
popupRoot,
popupRoot,
popupRoot,
popupRoot,
target,
target,
null,
},
templatedParents);
}
}
}
[Fact]
public void Should_Not_Overwrite_TemplatedParent_Of_Item_In_ItemsControl_With_Popup_On_Second_Open()
{
// Test uses OverlayPopupHost default template
using (CreateServices())
{
PopupItemsControl target;
var item = new Border();
var root = PreparedWindow(target = new PopupItemsControl
{
Items = new[] { item },
Template = new FuncControlTemplate<PopupItemsControl>(PopupItemsControlTemplate),
});
root.Show();
target.ApplyTemplate();
var popup = (Popup)target.GetTemplateChildren().First(x => x.Name == "popup");
popup.Open();
var popupRoot = (Control)popup.Host;
popupRoot.Measure(Size.Infinity);
popupRoot.Arrange(new Rect(popupRoot.DesiredSize));
Assert.Null(item.TemplatedParent);
popup.Close();
popup.Open();
Assert.Null(item.TemplatedParent);
}
}
[Fact]
public void DataContextBeginUpdate_Should_Not_Be_Called_For_Controls_That_Dont_Inherit()
{
@ -979,10 +1107,27 @@ namespace Avalonia.Controls.UnitTests.Primitives
}.RegisterInNameScope(scope);
}
private static IControl PopupItemsControlTemplate(PopupItemsControl control, INameScope scope)
{
return new Popup
{
Name = "popup",
PlacementTarget = control,
Child = new ItemsPresenter
{
[~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
}
}.RegisterInNameScope(scope);
}
private class PopupContentControl : ContentControl
{
}
private class PopupItemsControl : ItemsControl
{
}
private class TestControl : Decorator
{
public event EventHandler DataContextBeginUpdate;

Loading…
Cancel
Save