Browse Source

Added ItemContainerTheme property.

Otherwise there's no easy way to apply themes to item containers.
pull/8263/head
Steven Kirk 4 years ago
parent
commit
05c1f58308
  1. 6
      src/Avalonia.Controls/Generators/IItemContainerGenerator.cs
  2. 16
      src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
  3. 21
      src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
  4. 5
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  5. 21
      src/Avalonia.Controls/ItemsControl.cs
  6. 5
      src/Avalonia.Controls/TreeViewItem.cs
  7. 28
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  8. 44
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  9. 29
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

6
src/Avalonia.Controls/Generators/IItemContainerGenerator.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls.Templates;
using Avalonia.Styling;
namespace Avalonia.Controls.Generators
{
@ -14,6 +15,11 @@ namespace Avalonia.Controls.Generators
/// </summary>
IEnumerable<ItemContainerInfo> Containers { get; }
/// <summary>
/// Gets or sets the theme to be applied to the items in the control.
/// </summary>
ControlTheme? ItemContainerTheme { get; set; }
/// <summary>
/// Gets or sets the data template used to display the items in the control.
/// </summary>

16
src/Avalonia.Controls/Generators/ItemContainerGenerator.cs

@ -4,6 +4,7 @@ using System.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Styling;
namespace Avalonia.Controls.Generators
{
@ -35,6 +36,11 @@ namespace Avalonia.Controls.Generators
/// <inheritdoc/>
public event EventHandler<ItemContainerEventArgs>? Recycled;
/// <summary>
/// Gets or sets the theme to be applied to the items in the control.
/// </summary>
public ControlTheme? ItemContainerTheme { get; set; }
/// <summary>
/// Gets or sets the data template used to display the items in the control.
/// </summary>
@ -190,10 +196,18 @@ namespace Avalonia.Controls.Generators
result.SetValue(
ContentPresenter.ContentTemplateProperty,
ItemTemplate,
BindingPriority.TemplatedParent);
BindingPriority.Style);
}
}
if (ItemContainerTheme != null)
{
result.SetValue(
StyledElement.ThemeProperty,
ItemContainerTheme,
BindingPriority.TemplatedParent);
}
return result;
}

21
src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs

@ -44,28 +44,29 @@ namespace Avalonia.Controls.Generators
{
var container = item as T;
if (container != null)
if (container is null)
{
return container;
}
else
{
var result = new T();
container = new T();
if (ContentTemplateProperty != null)
{
result.SetValue(ContentTemplateProperty, ItemTemplate, BindingPriority.Style);
container.SetValue(ContentTemplateProperty, ItemTemplate, BindingPriority.Style);
}
result.SetValue(ContentProperty, item, BindingPriority.Style);
container.SetValue(ContentProperty, item, BindingPriority.Style);
if (!(item is IControl))
{
result.DataContext = item;
container.DataContext = item;
}
}
return result;
if (ItemContainerTheme != null)
{
container.SetValue(StyledElement.ThemeProperty, ItemContainerTheme, BindingPriority.Style);
}
return container;
}
/// <inheritdoc/>

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

@ -71,6 +71,11 @@ namespace Avalonia.Controls.Generators
var template = GetTreeDataTemplate(item, ItemTemplate);
var result = new T();
if (ItemContainerTheme != null)
{
result.SetValue(Control.ThemeProperty, ItemContainerTheme, BindingPriority.Style);
}
result.SetValue(ContentProperty, template.Build(item), BindingPriority.Style);
var itemsSelector = template.ItemsSelector(item);

21
src/Avalonia.Controls/ItemsControl.cs

@ -15,6 +15,7 @@ using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
using Avalonia.VisualTree;
using Avalonia.Styling;
namespace Avalonia.Controls
{
@ -36,6 +37,12 @@ namespace Avalonia.Controls
public static readonly DirectProperty<ItemsControl, IEnumerable?> ItemsProperty =
AvaloniaProperty.RegisterDirect<ItemsControl, IEnumerable?>(nameof(Items), o => o.Items, (o, v) => o.Items = v);
/// <summary>
/// Defines the <see cref="ItemContainerTheme"/> property.
/// </summary>
public static readonly StyledProperty<ControlTheme?> ItemContainerThemeProperty =
AvaloniaProperty.Register<ItemsControl, ControlTheme?>(nameof(ItemContainerTheme));
/// <summary>
/// Defines the <see cref="ItemCount"/> property.
/// </summary>
@ -88,6 +95,7 @@ namespace Avalonia.Controls
{
_itemContainerGenerator = CreateItemContainerGenerator();
_itemContainerGenerator.ItemContainerTheme = ItemContainerTheme;
_itemContainerGenerator.ItemTemplate = ItemTemplate;
_itemContainerGenerator.Materialized += (_, e) => OnContainersMaterialized(e);
_itemContainerGenerator.Dematerialized += (_, e) => OnContainersDematerialized(e);
@ -108,6 +116,15 @@ namespace Avalonia.Controls
set { SetAndRaise(ItemsProperty, ref _items, value); }
}
/// <summary>
/// Gets or sets the <see cref="ControlTheme"/> that is applied to the container element generated for each item.
/// </summary>
public ControlTheme? ItemContainerTheme
{
get { return GetValue(ItemContainerThemeProperty); }
set { SetValue(ItemContainerThemeProperty, value); }
}
/// <summary>
/// Gets the number of items in <see cref="Items"/>.
/// </summary>
@ -349,6 +366,10 @@ namespace Avalonia.Controls
{
UpdatePseudoClasses(change.GetNewValue<int>());
}
else if (change.Property == ItemContainerThemeProperty && _itemContainerGenerator is not null)
{
_itemContainerGenerator.ItemContainerTheme = change.GetNewValue<ControlTheme?>();
}
}
/// <summary>

5
src/Avalonia.Controls/TreeViewItem.cs

@ -121,6 +121,11 @@ namespace Avalonia.Controls
{
ItemTemplate = _treeView.ItemTemplate;
}
if (ItemContainerTheme == null && _treeView?.ItemContainerTheme != null)
{
ItemContainerTheme = _treeView.ItemContainerTheme;
}
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)

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

@ -1,15 +1,14 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.VisualTree;
using Xunit;
using System.Collections.ObjectModel;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.Input;
using System.Collections.Generic;
using Xunit;
namespace Avalonia.Controls.UnitTests
{
@ -62,6 +61,25 @@ namespace Avalonia.Controls.UnitTests
Assert.Null(container.TemplatedParent);
}
[Fact]
public void Container_Should_Have_Theme_Set_To_ItemContainerTheme()
{
var theme = new ControlTheme();
var target = new ItemsControl
{
ItemContainerTheme = theme,
};
target.Template = GetTemplate();
target.Items = new[] { "Foo" };
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
var container = (ContentPresenter)target.Presenter.Panel.Children[0];
Assert.Same(container.Theme, theme);
}
[Fact]
public void Container_Should_Have_LogicalParent_Set_To_ItemsControl()
{

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

@ -94,6 +94,50 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Container_Should_Have_Theme_Set_To_ItemContainerTheme()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var items = new[] { "Foo", "Bar", "Baz " };
var theme = new ControlTheme();
var target = new ListBox
{
Template = ListBoxTemplate(),
Items = items,
ItemContainerTheme = theme,
};
Prepare(target);
var container = (ListBoxItem)target.Presenter.Panel.Children[0];
Assert.Same(container.Theme, theme);
}
}
[Fact]
public void Inline_Item_Should_Have_Theme_Set_To_ItemContainerTheme()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var items = new[] { "Foo", "Bar", "Baz " };
var theme = new ControlTheme();
var target = new ListBox
{
Template = ListBoxTemplate(),
Items = new[] { new ListBoxItem() },
ItemContainerTheme = theme,
};
Prepare(target);
var container = (ListBoxItem)target.Presenter.Panel.Children[0];
Assert.Same(container.Theme, theme);
}
}
[Fact]
public void LogicalChildren_Should_Be_Set_For_DataTemplate_Generated_Items()
{

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

@ -72,6 +72,35 @@ namespace Avalonia.Controls.UnitTests
Assert.All(items, x => Assert.IsType<Canvas>(x.HeaderPresenter.Child));
}
[Fact]
public void Items_Should_Be_Created_Using_ItemConatinerTheme_If_Present()
{
TreeView target;
var theme = new ControlTheme();
var root = new TestRoot
{
Child = target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = CreateTestTreeData(),
ItemContainerTheme = theme,
ItemTemplate = new FuncTreeDataTemplate<Node>(
(_, __) => new Canvas(),
x => x.Children),
}
};
ApplyTemplates(target);
var items = target.ItemContainerGenerator.Index.Containers
.OfType<TreeViewItem>()
.ToList();
Assert.Equal(5, items.Count);
Assert.All(items, x => Assert.Same(theme, x.ItemContainerTheme));
}
[Fact]
public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers()
{

Loading…
Cancel
Save