diff --git a/.ncrunch/SafeAreaDemo.Android.v3.ncrunchproject b/.ncrunch/SafeAreaDemo.Android.v3.ncrunchproject
new file mode 100644
index 00000000000..319cd523cec
--- /dev/null
+++ b/.ncrunch/SafeAreaDemo.Android.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject b/.ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject
new file mode 100644
index 00000000000..319cd523cec
--- /dev/null
+++ b/.ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject b/.ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject
new file mode 100644
index 00000000000..319cd523cec
--- /dev/null
+++ b/.ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/SafeAreaDemo.v3.ncrunchproject b/.ncrunch/SafeAreaDemo.v3.ncrunchproject
new file mode 100644
index 00000000000..319cd523cec
--- /dev/null
+++ b/.ncrunch/SafeAreaDemo.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 60b0f8b193c..1c62de9bed6 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -378,48 +378,48 @@ protected internal virtual void PrepareContainerForItemOverride(Control containe
if (container is HeaderedContentControl hcc)
{
- hcc.Content = item;
+ SetIfUnset(hcc, HeaderedContentControl.ContentProperty, item);
if (item is IHeadered headered)
- hcc.Header = headered.Header;
+ SetIfUnset(hcc, HeaderedContentControl.HeaderProperty, headered.Header);
else if (item is not Visual)
- hcc.Header = item;
+ SetIfUnset(hcc, HeaderedContentControl.HeaderProperty, item);
if (itemTemplate is not null)
- hcc.HeaderTemplate = itemTemplate;
+ SetIfUnset(hcc, HeaderedContentControl.HeaderTemplateProperty, itemTemplate);
}
else if (container is ContentControl cc)
{
- cc.Content = item;
+ SetIfUnset(cc, ContentControl.ContentProperty, item);
if (itemTemplate is not null)
- cc.ContentTemplate = itemTemplate;
+ SetIfUnset(cc, ContentControl.ContentTemplateProperty, itemTemplate);
}
else if (container is ContentPresenter p)
{
- p.Content = item;
+ SetIfUnset(p, ContentPresenter.ContentProperty, item);
if (itemTemplate is not null)
- p.ContentTemplate = itemTemplate;
+ SetIfUnset(p, ContentPresenter.ContentTemplateProperty, itemTemplate);
}
else if (container is ItemsControl ic)
{
if (itemTemplate is not null)
- ic.ItemTemplate = itemTemplate;
- if (ItemContainerTheme is { } ict && !ict.IsSet(ItemContainerThemeProperty))
- ic.ItemContainerTheme = ict;
+ SetIfUnset(ic, ItemTemplateProperty, itemTemplate);
+ if (ItemContainerTheme is { } ict)
+ SetIfUnset(ic, ItemContainerThemeProperty, ict);
}
// These conditions are separate because HeaderedItemsControl and
// HeaderedSelectingItemsControl also need to run the ItemsControl preparation.
if (container is HeaderedItemsControl hic)
{
- hic.Header = item;
- hic.HeaderTemplate = itemTemplate;
+ SetIfUnset(hic, HeaderedItemsControl.HeaderProperty, item);
+ SetIfUnset(hic, HeaderedItemsControl.HeaderTemplateProperty, itemTemplate);
hic.PrepareItemContainer(this);
}
else if (container is HeaderedSelectingItemsControl hsic)
{
- hsic.Header = item;
- hsic.HeaderTemplate = itemTemplate;
+ SetIfUnset(hsic, HeaderedSelectingItemsControl.HeaderProperty, item);
+ SetIfUnset(hsic, HeaderedSelectingItemsControl.HeaderTemplateProperty, itemTemplate);
hsic.PrepareItemContainer(this);
}
}
@@ -458,30 +458,35 @@ protected internal virtual void ClearContainerForItemOverride(Control container)
{
if (container is HeaderedContentControl hcc)
{
- if (hcc.Content is Control)
- hcc.Content = null;
- if (hcc.Header is Control)
- hcc.Header = null;
+ hcc.ClearValue(HeaderedContentControl.ContentProperty);
+ hcc.ClearValue(HeaderedContentControl.HeaderProperty);
+ hcc.ClearValue(HeaderedContentControl.HeaderTemplateProperty);
}
else if (container is ContentControl cc)
{
- if (cc.Content is Control)
- cc.Content = null;
+ cc.ClearValue(ContentControl.ContentProperty);
+ cc.ClearValue(ContentControl.ContentTemplateProperty);
}
else if (container is ContentPresenter p)
{
- if (p.Content is Control)
- p.Content = null;
+ p.ClearValue(ContentPresenter.ContentProperty);
+ p.ClearValue(ContentPresenter.ContentTemplateProperty);
}
- else if (container is HeaderedItemsControl hic)
+ else if (container is ItemsControl ic)
+ {
+ ic.ClearValue(ItemTemplateProperty);
+ ic.ClearValue(ItemContainerThemeProperty);
+ }
+
+ if (container is HeaderedItemsControl hic)
{
- if (hic.Header is Control)
- hic.Header = null;
+ hic.ClearValue(HeaderedItemsControl.HeaderProperty);
+ hic.ClearValue(HeaderedItemsControl.HeaderTemplateProperty);
}
else if (container is HeaderedSelectingItemsControl hsic)
{
- if (hsic.Header is Control)
- hsic.Header = null;
+ hsic.ClearValue(HeaderedSelectingItemsControl.HeaderProperty);
+ hsic.ClearValue(HeaderedSelectingItemsControl.HeaderTemplateProperty);
}
// Feels like we should be clearing the HeaderedItemsControl.Items binding here, but looking at
@@ -707,6 +712,12 @@ private void AddControlItemsToLogicalChildren(IEnumerable? items)
LogicalChildren.AddRange(toAdd);
}
+ private void SetIfUnset(AvaloniaObject target, StyledProperty property, T value)
+ {
+ if (!target.IsSet(property))
+ target.SetCurrentValue(property, value);
+ }
+
private void RemoveControlItemsFromLogicalChildren(IEnumerable? items)
{
if (items is null)
diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
index 7a227a48ab1..84eed5ec82b 100644
--- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
@@ -554,6 +554,36 @@ public void Initial_Binding_Of_SelectedItems_Should_Not_Cause_Write_To_SelectedI
Assert.Equal(new[] { "Bar" }, target.Selection.SelectedItems);
}
+ [Fact]
+ public void Content_Can_Be_Bound_In_ItemContainerTheme()
+ {
+ using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
+ {
+ var items = new[] { new ItemViewModel("Foo"), new ItemViewModel("Bar") };
+ var theme = new ControlTheme(typeof(ListBoxItem))
+ {
+ Setters =
+ {
+ new Setter(ListBoxItem.ContentProperty, new Binding("Caption")),
+ }
+ };
+
+ var target = new ListBox
+ {
+ Template = ListBoxTemplate(),
+ ItemsSource = items,
+ ItemContainerTheme = theme,
+ };
+
+ Prepare(target);
+
+ var containers = target.GetRealizedContainers().Cast().ToList();
+ Assert.Equal(2, containers.Count);
+ Assert.Equal("Foo", containers[0].Content);
+ Assert.Equal("Bar", containers[1].Content);
+ }
+ }
+
private static FuncControlTemplate ListBoxTemplate()
{
return new FuncControlTemplate((parent, scope) =>
@@ -918,6 +948,8 @@ public void ContainerClearing_Is_Raised_When_Item_Removed()
Assert.Equal(1, raised);
}
+ private record ItemViewModel(string Caption);
+
private class ResettingCollection : List, INotifyCollectionChanged
{
public ResettingCollection(int itemCount)
diff --git a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs
index bd59183e926..08aedceac35 100644
--- a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs
@@ -1,16 +1,16 @@
using System;
+using System.Collections;
using System.Collections.Generic;
-using System.Text;
+using System.Linq;
using System.Windows.Input;
-using Avalonia.Collections;
using Avalonia.Controls.Presenters;
-using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
+using Avalonia.Controls.Utils;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Platform;
+using Avalonia.Styling;
using Avalonia.UnitTests;
-using Avalonia.VisualTree;
using Moq;
using Xunit;
@@ -36,7 +36,6 @@ public void Separator_Item_Should_Set_Focusable_False()
Assert.False(target.Focusable);
}
-
[Fact]
public void MenuItem_Is_Disabled_When_Command_Is_Enabled_But_IsEnabled_Is_False()
{
@@ -393,6 +392,87 @@ public void Menu_ItemTemplate_Should_Be_Applied_To_TopLevel_MenuItem_Header()
}
}
+ [Fact]
+ public void Header_And_ItemsSource_Can_Be_Bound_In_Style()
+ {
+ using var app = Application();
+ var items = new[]
+ {
+ new MenuViewModel("Foo")
+ {
+ Children = new[]
+ {
+ new MenuViewModel("FooChild"),
+ },
+ },
+ new MenuViewModel("Bar"),
+ };
+
+ var target = new Menu
+ {
+ ItemsSource = items,
+ Styles =
+ {
+ new Style(x => x.OfType