Browse Source
A bunch of tests still failing, and some code commented out, but outlines the new API shape.pull/9677/head
34 changed files with 2670 additions and 3772 deletions
@ -1,114 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Controls.Templates; |
|||
using Avalonia.Data; |
|||
using Avalonia.Styling; |
|||
|
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Creates containers for items and maintains a list of created containers.
|
|||
/// </summary>
|
|||
public interface IItemContainerGenerator |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the currently realized containers.
|
|||
/// </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>
|
|||
IDataTemplate? ItemTemplate { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the binding to use to bind to the member of an item used for displaying
|
|||
/// </summary>
|
|||
IBinding? DisplayMemberBinding { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the ContainerType, or null if its an untyped ContainerGenerator.
|
|||
/// </summary>
|
|||
Type? ContainerType { get; } |
|||
|
|||
/// <summary>
|
|||
/// Signaled whenever new containers are materialized.
|
|||
/// </summary>
|
|||
event EventHandler<ItemContainerEventArgs>? Materialized; |
|||
|
|||
/// <summary>
|
|||
/// Event raised whenever containers are dematerialized.
|
|||
/// </summary>
|
|||
event EventHandler<ItemContainerEventArgs>? Dematerialized; |
|||
|
|||
/// <summary>
|
|||
/// Event raised whenever containers are recycled.
|
|||
/// </summary>
|
|||
event EventHandler<ItemContainerEventArgs>? Recycled; |
|||
|
|||
/// <summary>
|
|||
/// Creates a container control for an item.
|
|||
/// </summary>
|
|||
/// <param name="index">
|
|||
/// The index of the item of data in the control's items.
|
|||
/// </param>
|
|||
/// <param name="item">The item.</param>
|
|||
/// <returns>The created controls.</returns>
|
|||
ItemContainerInfo Materialize(int index, object item); |
|||
|
|||
/// <summary>
|
|||
/// Removes a set of created containers.
|
|||
/// </summary>
|
|||
/// <param name="startingIndex">
|
|||
/// The index of the first item in the control's items.
|
|||
/// </param>
|
|||
/// <param name="count">The the number of items to remove.</param>
|
|||
/// <returns>The removed containers.</returns>
|
|||
IEnumerable<ItemContainerInfo> Dematerialize(int startingIndex, int count); |
|||
|
|||
/// <summary>
|
|||
/// Inserts space for newly inserted containers in the index.
|
|||
/// </summary>
|
|||
/// <param name="index">The index at which space should be inserted.</param>
|
|||
/// <param name="count">The number of blank spaces to create.</param>
|
|||
void InsertSpace(int index, int count); |
|||
|
|||
/// <summary>
|
|||
/// Removes a set of created containers and updates the index of later containers to fill
|
|||
/// the gap.
|
|||
/// </summary>
|
|||
/// <param name="startingIndex">
|
|||
/// The index of the first item in the control's items.
|
|||
/// </param>
|
|||
/// <param name="count">The the number of items to remove.</param>
|
|||
/// <returns>The removed containers.</returns>
|
|||
IEnumerable<ItemContainerInfo> RemoveRange(int startingIndex, int count); |
|||
|
|||
bool TryRecycle(int oldIndex, int newIndex, object item); |
|||
|
|||
/// <summary>
|
|||
/// Clears all created containers and returns the removed controls.
|
|||
/// </summary>
|
|||
/// <returns>The removed controls.</returns>
|
|||
IEnumerable<ItemContainerInfo> Clear(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the container control representing the item with the specified index.
|
|||
/// </summary>
|
|||
/// <param name="index">The index.</param>
|
|||
/// <returns>The container, or null if no container created.</returns>
|
|||
Control? ContainerFromIndex(int index); |
|||
|
|||
/// <summary>
|
|||
/// Gets the index of the specified container control.
|
|||
/// </summary>
|
|||
/// <param name="container">The container.</param>
|
|||
/// <returns>The index of the container, or -1 if not found.</returns>
|
|||
int IndexFromContainer(Control? container); |
|||
} |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Creates containers for tree items and maintains a list of created containers.
|
|||
/// </summary>
|
|||
public interface ITreeItemContainerGenerator : IItemContainerGenerator |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the container index for the tree.
|
|||
/// </summary>
|
|||
TreeContainerIndex? Index { get; } |
|||
|
|||
/// <summary>
|
|||
/// Updates the index based on the parent <see cref="TreeView"/>.
|
|||
/// </summary>
|
|||
void UpdateIndex(); |
|||
} |
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Provides details for the <see cref="IItemContainerGenerator.Materialized"/>
|
|||
/// and <see cref="IItemContainerGenerator.Dematerialized"/> events.
|
|||
/// </summary>
|
|||
public class ItemContainerEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ItemContainerEventArgs"/> class.
|
|||
/// </summary>
|
|||
/// <param name="container">The container.</param>
|
|||
public ItemContainerEventArgs(ItemContainerInfo container) |
|||
{ |
|||
StartingIndex = container.Index; |
|||
Containers = new[] { container }; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ItemContainerEventArgs"/> class.
|
|||
/// </summary>
|
|||
/// <param name="startingIndex">The index of the first container in the source items.</param>
|
|||
/// <param name="containers">The containers.</param>
|
|||
/// <remarks>
|
|||
/// TODO: Do we really need to pass in StartingIndex here? The ItemContainerInfo objects
|
|||
/// have an index, and what happens if the contains passed in aren't sequential?
|
|||
/// </remarks>
|
|||
public ItemContainerEventArgs( |
|||
int startingIndex, |
|||
IList<ItemContainerInfo> containers) |
|||
{ |
|||
StartingIndex = startingIndex; |
|||
Containers = containers; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the containers.
|
|||
/// </summary>
|
|||
public IList<ItemContainerInfo> Containers { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the index of the first container in the source items.
|
|||
/// </summary>
|
|||
public int StartingIndex { get; } |
|||
} |
|||
} |
|||
@ -1,262 +1,68 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Avalonia.Controls.Presenters; |
|||
using Avalonia.Controls.Templates; |
|||
using Avalonia.Data; |
|||
using Avalonia.Styling; |
|||
|
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Creates containers for items and maintains a list of created containers.
|
|||
/// Generates containers for an <see cref="ItemsControl"/>.
|
|||
/// </summary>
|
|||
public class ItemContainerGenerator : IItemContainerGenerator |
|||
/// <remarks>
|
|||
/// Although this class is similar to that found in WPF/UWP, in Avalonia this class only
|
|||
/// concerns itself with generating and clearing item containers; it does not maintain a
|
|||
/// record of the currently realized containers, that responsibility is delegated to the
|
|||
/// items panel.
|
|||
/// </remarks>
|
|||
public class ItemContainerGenerator |
|||
{ |
|||
private SortedDictionary<int, ItemContainerInfo> _containers = new SortedDictionary<int, ItemContainerInfo>(); |
|||
private ItemsControl _owner; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ItemContainerGenerator"/> class.
|
|||
/// </summary>
|
|||
/// <param name="owner">The owner control.</param>
|
|||
public ItemContainerGenerator(Control owner) |
|||
{ |
|||
Owner = owner ?? throw new ArgumentNullException(nameof(owner)); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public IEnumerable<ItemContainerInfo> Containers => _containers.Values; |
|||
|
|||
/// <inheritdoc/>
|
|||
public event EventHandler<ItemContainerEventArgs>? Materialized; |
|||
|
|||
/// <inheritdoc/>
|
|||
public event EventHandler<ItemContainerEventArgs>? Dematerialized; |
|||
|
|||
/// <inheritdoc/>
|
|||
public event EventHandler<ItemContainerEventArgs>? Recycled; |
|||
internal ItemContainerGenerator(ItemsControl owner) => _owner = owner; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the theme to be applied to the items in the control.
|
|||
/// Creates a new container control.
|
|||
/// </summary>
|
|||
public ControlTheme? ItemContainerTheme { get; set; } |
|||
/// <returns>The newly created container control.</returns>
|
|||
/// <remarks>
|
|||
/// Before calling this method, <see cref="IsItemItsOwnContainer(Control)"/> should be
|
|||
/// called to determine whether the item itself should be used as a container. After
|
|||
/// calling this method, <see cref="PrepareItemContainer(Control, object, int)"/> should
|
|||
/// be called to prepare the container to display the specified item.
|
|||
/// </remarks>
|
|||
public Control CreateContainer() => _owner.CreateContainerOverride(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the data template used to display the items in the control.
|
|||
/// Determines whether the specified item is (or is eligible to be) its own container.
|
|||
/// </summary>
|
|||
public IDataTemplate? ItemTemplate { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public IBinding? DisplayMemberBinding { get; set; } |
|||
/// <param name="container">The item.</param>
|
|||
/// <returns>true if the item is its own container, otherwise false.</returns>
|
|||
/// <remarks>
|
|||
/// Whereas in WPF/UWP, non-control items can be their own container, in Avalonia only
|
|||
/// control items may be; the caller is responsible for checking if each item is a control
|
|||
/// and calling this method before creating a new container.
|
|||
/// </remarks>
|
|||
public bool IsItemItsOwnContainer(Control container) => _owner.IsItemItsOwnContainerOverride(container); |
|||
|
|||
/// <summary>
|
|||
/// Gets the owner control.
|
|||
/// Prepares the specified element as the container for the corresponding item.
|
|||
/// </summary>
|
|||
public Control Owner { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual Type? ContainerType => null; |
|||
|
|||
/// <inheritdoc/>
|
|||
public ItemContainerInfo Materialize(int index, object item) |
|||
{ |
|||
var container = new ItemContainerInfo(CreateContainer(item)!, item, index); |
|||
|
|||
_containers.Add(container.Index, container); |
|||
Materialized?.Invoke(this, new ItemContainerEventArgs(container)); |
|||
|
|||
return container; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual IEnumerable<ItemContainerInfo> Dematerialize(int startingIndex, int count) |
|||
{ |
|||
var result = new List<ItemContainerInfo>(); |
|||
|
|||
for (int i = startingIndex; i < startingIndex + count; ++i) |
|||
{ |
|||
result.Add(_containers[i]); |
|||
_containers.Remove(i); |
|||
} |
|||
|
|||
Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result)); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual void InsertSpace(int index, int count) |
|||
{ |
|||
if (count > 0) |
|||
{ |
|||
var toMove = _containers.Where(x => x.Key >= index) |
|||
.OrderByDescending(x => x.Key) |
|||
.ToArray(); |
|||
|
|||
foreach (var i in toMove) |
|||
{ |
|||
_containers.Remove(i.Key); |
|||
i.Value.Index += count; |
|||
_containers.Add(i.Value.Index, i.Value); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual IEnumerable<ItemContainerInfo> RemoveRange(int startingIndex, int count) |
|||
{ |
|||
var result = new List<ItemContainerInfo>(); |
|||
|
|||
if (count > 0) |
|||
{ |
|||
for (var i = startingIndex; i < startingIndex + count; ++i) |
|||
{ |
|||
if (_containers.TryGetValue(i, out var found)) |
|||
{ |
|||
result.Add(found); |
|||
} |
|||
|
|||
_containers.Remove(i); |
|||
} |
|||
|
|||
var toMove = _containers.Where(x => x.Key >= startingIndex) |
|||
.OrderBy(x => x.Key).ToArray(); |
|||
|
|||
foreach (var i in toMove) |
|||
{ |
|||
_containers.Remove(i.Key); |
|||
i.Value.Index -= count; |
|||
_containers.Add(i.Value.Index, i.Value); |
|||
} |
|||
|
|||
Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result)); |
|||
|
|||
if (toMove.Length > 0) |
|||
{ |
|||
var containers = toMove.Select(x => x.Value).ToArray(); |
|||
Recycled?.Invoke(this, new ItemContainerEventArgs(containers[0].Index, containers)); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual bool TryRecycle(int oldIndex, int newIndex, object item) => false; |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual IEnumerable<ItemContainerInfo> Clear() |
|||
{ |
|||
var result = Containers.ToArray(); |
|||
_containers.Clear(); |
|||
|
|||
if (result.Length > 0) |
|||
{ |
|||
Dematerialized?.Invoke(this, new ItemContainerEventArgs(0, result)); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public Control? ContainerFromIndex(int index) |
|||
{ |
|||
ItemContainerInfo? result; |
|||
_containers.TryGetValue(index, out result); |
|||
return result?.ContainerControl; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public int IndexFromContainer(Control? container) |
|||
{ |
|||
foreach (var i in _containers) |
|||
{ |
|||
if (i.Value.ContainerControl == container) |
|||
{ |
|||
return i.Key; |
|||
} |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
/// <param name="container">The element that's used to display the specified item.</param>
|
|||
/// <param name="item">The item to display.</param>
|
|||
/// <param name="index">The index of the item to display.</param>
|
|||
/// <remarks>
|
|||
/// If <see cref="IsItemItsOwnContainer(Control)"/> is true for an item, then this method
|
|||
/// only needs to be called a single time, otherwise this method should be called after the
|
|||
/// container is created, and each subsequent time the container is recycled to display a
|
|||
/// new item.
|
|||
/// </remarks>
|
|||
public void PrepareItemContainer(Control container, object? item, int index) => |
|||
_owner.PrepareItemContainer(container, item, index); |
|||
|
|||
/// <summary>
|
|||
/// Creates the container for an item.
|
|||
/// Undoes the effects of the <see cref="PrepareItemContainer(Control, object, int)"/> method.
|
|||
/// </summary>
|
|||
/// <param name="item">The item.</param>
|
|||
/// <returns>The created container control.</returns>
|
|||
protected virtual Control? CreateContainer(object item) |
|||
{ |
|||
var result = item as Control; |
|||
|
|||
if (result == null) |
|||
{ |
|||
result = new ContentPresenter(); |
|||
if (DisplayMemberBinding is not null) |
|||
{ |
|||
result.SetValue(StyledElement.DataContextProperty, item, BindingPriority.Style); |
|||
result.Bind(ContentPresenter.ContentProperty, DisplayMemberBinding, BindingPriority.Style); |
|||
} |
|||
else |
|||
{ |
|||
result.SetValue(ContentPresenter.ContentProperty, item, BindingPriority.Style); |
|||
} |
|||
|
|||
if (ItemTemplate != null) |
|||
{ |
|||
result.SetValue( |
|||
ContentPresenter.ContentTemplateProperty, |
|||
ItemTemplate, |
|||
BindingPriority.Style); |
|||
} |
|||
} |
|||
/// <param name="container">The element that's used to display the specified item.</param>
|
|||
public void ClearItemContainer(Control container) => _owner.ClearContainerForItemOverride(container); |
|||
|
|||
if (ItemContainerTheme != null) |
|||
{ |
|||
result.SetValue( |
|||
StyledElement.ThemeProperty, |
|||
ItemContainerTheme, |
|||
BindingPriority.Template); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Moves a container.
|
|||
/// </summary>
|
|||
/// <param name="oldIndex">The old index.</param>
|
|||
/// <param name="newIndex">The new index.</param>
|
|||
/// <param name="item">The new item.</param>
|
|||
/// <returns>The container info.</returns>
|
|||
protected ItemContainerInfo MoveContainer(int oldIndex, int newIndex, object item) |
|||
{ |
|||
var container = _containers[oldIndex]; |
|||
container.Index = newIndex; |
|||
container.Item = item; |
|||
_containers.Remove(oldIndex); |
|||
_containers.Add(newIndex, container); |
|||
return container; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets all containers with an index that fall within a range.
|
|||
/// </summary>
|
|||
/// <param name="index">The first index.</param>
|
|||
/// <param name="count">The number of elements in the range.</param>
|
|||
/// <returns>The containers.</returns>
|
|||
protected IEnumerable<ItemContainerInfo> GetContainerRange(int index, int count) |
|||
{ |
|||
return _containers.Where(x => x.Key >= index && x.Key < index + count).Select(x => x.Value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raises the <see cref="Recycled"/> event.
|
|||
/// </summary>
|
|||
/// <param name="e">The event args.</param>
|
|||
protected void RaiseRecycled(ItemContainerEventArgs e) |
|||
{ |
|||
Recycled?.Invoke(this, e); |
|||
} |
|||
public Control? ContainerFromIndex(int index) => _owner.ContainerFromIndex(index); |
|||
public int IndexFromContainer(Control container) => _owner.IndexFromContainer(container); |
|||
} |
|||
} |
|||
|
|||
@ -1,102 +0,0 @@ |
|||
using System; |
|||
using Avalonia.Data; |
|||
|
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Creates containers for items and maintains a list of created containers.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the container.</typeparam>
|
|||
public class ItemContainerGenerator<T> : ItemContainerGenerator where T : Control, new() |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ItemContainerGenerator{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="owner">The owner control.</param>
|
|||
/// <param name="contentProperty">The container's Content property.</param>
|
|||
/// <param name="contentTemplateProperty">The container's ContentTemplate property.</param>
|
|||
public ItemContainerGenerator( |
|||
Control owner, |
|||
AvaloniaProperty contentProperty, |
|||
AvaloniaProperty? contentTemplateProperty) |
|||
: base(owner) |
|||
{ |
|||
ContentProperty = contentProperty ?? throw new ArgumentNullException(nameof(contentProperty)); |
|||
ContentTemplateProperty = contentTemplateProperty; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Type ContainerType => typeof(T); |
|||
|
|||
/// <summary>
|
|||
/// Gets the container's Content property.
|
|||
/// </summary>
|
|||
protected AvaloniaProperty ContentProperty { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the container's ContentTemplate property.
|
|||
/// </summary>
|
|||
protected AvaloniaProperty? ContentTemplateProperty { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override Control? CreateContainer(object item) |
|||
{ |
|||
var container = item as T; |
|||
|
|||
if (container is null) |
|||
{ |
|||
container = new T(); |
|||
|
|||
if (ContentTemplateProperty != null) |
|||
{ |
|||
container.SetValue(ContentTemplateProperty, ItemTemplate, BindingPriority.Style); |
|||
} |
|||
|
|||
if (DisplayMemberBinding is not null) |
|||
{ |
|||
container.SetValue(StyledElement.DataContextProperty, item, BindingPriority.Style); |
|||
container.Bind(ContentProperty, DisplayMemberBinding, BindingPriority.Style); |
|||
} |
|||
else |
|||
{ |
|||
container.SetValue(ContentProperty, item, BindingPriority.Style); |
|||
} |
|||
|
|||
if (!(item is Control)) |
|||
{ |
|||
container.DataContext = item; |
|||
} |
|||
} |
|||
|
|||
if (ItemContainerTheme != null) |
|||
{ |
|||
container.SetValue(StyledElement.ThemeProperty, ItemContainerTheme, BindingPriority.Style); |
|||
} |
|||
|
|||
return container; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool TryRecycle(int oldIndex, int newIndex, object item) |
|||
{ |
|||
var container = ContainerFromIndex(oldIndex); |
|||
|
|||
if (container == null) |
|||
{ |
|||
throw new IndexOutOfRangeException("Could not recycle container: not materialized."); |
|||
} |
|||
|
|||
container.SetValue(ContentProperty, item); |
|||
|
|||
if (!(item is Control)) |
|||
{ |
|||
container.DataContext = item; |
|||
} |
|||
|
|||
var info = MoveContainer(oldIndex, newIndex, item); |
|||
RaiseRecycled(new ItemContainerEventArgs(info)); |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -1,42 +0,0 @@ |
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Holds information about an item container generated by an
|
|||
/// <see cref="IItemContainerGenerator"/>.
|
|||
/// </summary>
|
|||
public class ItemContainerInfo |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ItemContainerInfo"/> class.
|
|||
/// </summary>
|
|||
/// <param name="container">The container control.</param>
|
|||
/// <param name="item">The item that the container represents.</param>
|
|||
/// <param name="index">
|
|||
/// The index of the item in the <see cref="ItemsControl.Items"/> collection.
|
|||
/// </param>
|
|||
public ItemContainerInfo(Control container, object item, int index) |
|||
{ |
|||
ContainerControl = container; |
|||
Item = item; |
|||
Index = index; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the container control.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This will be null if <see cref="Item"/> is null.
|
|||
/// </remarks>
|
|||
public Control ContainerControl { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the item that the container represents.
|
|||
/// </summary>
|
|||
public object Item { get; internal set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the index of the item in the <see cref="ItemsControl.Items"/> collection.
|
|||
/// </summary>
|
|||
public int Index { get; set; } |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
public class MenuItemContainerGenerator : ItemContainerGenerator<MenuItem> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ItemContainerGenerator{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="owner">The owner control.</param>
|
|||
public MenuItemContainerGenerator(Control owner) |
|||
: base(owner, MenuItem.HeaderProperty, null) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override Control? CreateContainer(object item) |
|||
{ |
|||
var separator = item as Separator; |
|||
return separator != null ? separator : base.CreateContainer(item); |
|||
} |
|||
} |
|||
} |
|||
@ -1,105 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Controls.Primitives; |
|||
using Avalonia.Controls.Templates; |
|||
using Avalonia.Data; |
|||
using Avalonia.LogicalTree; |
|||
using Avalonia.Reactive; |
|||
using Avalonia.VisualTree; |
|||
|
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
public class TabItemContainerGenerator : ItemContainerGenerator<TabItem> |
|||
{ |
|||
public TabItemContainerGenerator(TabControl owner) |
|||
: base(owner, ContentControl.ContentProperty, ContentControl.ContentTemplateProperty) |
|||
{ |
|||
Owner = owner; |
|||
} |
|||
|
|||
public new TabControl Owner { get; } |
|||
|
|||
protected override Control CreateContainer(object item) |
|||
{ |
|||
var tabItem = (TabItem)base.CreateContainer(item)!; |
|||
|
|||
tabItem.Bind(TabItem.TabStripPlacementProperty, new OwnerBinding<Dock>( |
|||
tabItem, |
|||
TabControl.TabStripPlacementProperty)); |
|||
|
|||
if (tabItem.HeaderTemplate == null) |
|||
{ |
|||
tabItem.Bind(TabItem.HeaderTemplateProperty, new OwnerBinding<IDataTemplate?>( |
|||
tabItem, |
|||
TabControl.ItemTemplateProperty)); |
|||
} |
|||
|
|||
if (Owner.HeaderDisplayMemberBinding is not null) |
|||
{ |
|||
tabItem.Bind(HeaderedContentControl.HeaderProperty, Owner.HeaderDisplayMemberBinding, |
|||
BindingPriority.Style); |
|||
} |
|||
|
|||
if (tabItem.Header == null) |
|||
{ |
|||
if (item is IHeadered headered) |
|||
{ |
|||
tabItem.Header = headered.Header; |
|||
} |
|||
else |
|||
{ |
|||
if (!(tabItem.DataContext is Control)) |
|||
{ |
|||
tabItem.Header = tabItem.DataContext; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!(tabItem.Content is Control)) |
|||
{ |
|||
tabItem.Bind(TabItem.ContentTemplateProperty, new OwnerBinding<IDataTemplate?>( |
|||
tabItem, |
|||
TabControl.ContentTemplateProperty)); |
|||
} |
|||
|
|||
return tabItem; |
|||
} |
|||
|
|||
private class OwnerBinding<T> : SingleSubscriberObservableBase<T> |
|||
{ |
|||
private readonly TabItem _item; |
|||
private readonly StyledProperty<T> _ownerProperty; |
|||
private IDisposable? _ownerSubscription; |
|||
private IDisposable? _propertySubscription; |
|||
|
|||
public OwnerBinding(TabItem item, StyledProperty<T> ownerProperty) |
|||
{ |
|||
_item = item; |
|||
_ownerProperty = ownerProperty; |
|||
} |
|||
|
|||
protected override void Subscribed() |
|||
{ |
|||
_ownerSubscription = ControlLocator.Track(_item, 0, typeof(TabControl)).Subscribe(OwnerChanged); |
|||
} |
|||
|
|||
protected override void Unsubscribed() |
|||
{ |
|||
_ownerSubscription?.Dispose(); |
|||
_ownerSubscription = null; |
|||
} |
|||
|
|||
private void OwnerChanged(ILogical? c) |
|||
{ |
|||
_propertySubscription?.Dispose(); |
|||
_propertySubscription = null; |
|||
|
|||
if (c is TabControl tabControl) |
|||
{ |
|||
_propertySubscription = tabControl.GetObservable(_ownerProperty) |
|||
.Subscribe(x => PublishNext(x)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,166 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Maintains an index of all item containers currently materialized by a <see cref="TreeView"/>.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Each <see cref="TreeViewItem"/> has its own <see cref="TreeItemContainerGenerator{T}"/>
|
|||
/// that maintains the list of its direct children, but they also share an instance of this
|
|||
/// class in their <see cref="TreeItemContainerGenerator{T}.Index"/> property which tracks
|
|||
/// the containers materialized for the entire tree.
|
|||
/// </remarks>
|
|||
public class TreeContainerIndex |
|||
{ |
|||
private readonly Dictionary<object, HashSet<Control>> _itemToContainerSet = new Dictionary<object, HashSet<Control>>(); |
|||
private readonly Dictionary<object, Control> _itemToContainer = new Dictionary<object, Control>(); |
|||
private readonly Dictionary<Control, object> _containerToItem = new Dictionary<Control, object>(); |
|||
|
|||
/// <summary>
|
|||
/// Signaled whenever new containers are materialized.
|
|||
/// </summary>
|
|||
public event EventHandler<ItemContainerEventArgs>? Materialized; |
|||
|
|||
/// <summary>
|
|||
/// Event raised whenever containers are dematerialized.
|
|||
/// </summary>
|
|||
public event EventHandler<ItemContainerEventArgs>? Dematerialized; |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently materialized containers.
|
|||
/// </summary>
|
|||
public IEnumerable<Control> Containers => _containerToItem.Keys; |
|||
|
|||
/// <summary>
|
|||
/// Gets the items of currently materialized containers.
|
|||
/// </summary>
|
|||
public IEnumerable<object> Items => _containerToItem.Values; |
|||
|
|||
/// <summary>
|
|||
/// Adds an entry to the index.
|
|||
/// </summary>
|
|||
/// <param name="item">The item.</param>
|
|||
/// <param name="container">The item container.</param>
|
|||
public void Add(object item, Control container) |
|||
{ |
|||
_itemToContainer[item] = container; |
|||
if (_itemToContainerSet.TryGetValue(item, out var set)) |
|||
{ |
|||
set.Add(container); |
|||
} |
|||
else |
|||
{ |
|||
_itemToContainerSet.Add(item, new HashSet<Control> { container }); |
|||
} |
|||
|
|||
_containerToItem.Add(container, item); |
|||
|
|||
Materialized?.Invoke( |
|||
this, |
|||
new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a container from private collections.
|
|||
/// </summary>
|
|||
/// <param name="container">The item container.</param>
|
|||
/// <param name="item">The DataContext object</param>
|
|||
private void RemoveContainer(Control container, object item) |
|||
{ |
|||
if (_itemToContainerSet.TryGetValue(item, out var set)) |
|||
{ |
|||
set.Remove(container); |
|||
if (set.Count == 0) |
|||
{ |
|||
_itemToContainerSet.Remove(item); |
|||
_itemToContainer.Remove(item); |
|||
} |
|||
else |
|||
{ |
|||
_itemToContainer[item] = set.First(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a container from the index.
|
|||
/// </summary>
|
|||
/// <param name="container">The item container.</param>
|
|||
public void Remove(Control container) |
|||
{ |
|||
var item = _containerToItem[container]; |
|||
_containerToItem.Remove(container); |
|||
RemoveContainer(container, item); |
|||
|
|||
Dematerialized?.Invoke( |
|||
this, |
|||
new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a set of containers from the index.
|
|||
/// </summary>
|
|||
/// <param name="startingIndex">The index of the first item.</param>
|
|||
/// <param name="containers">The item containers.</param>
|
|||
public void Remove(int startingIndex, IEnumerable<ItemContainerInfo> containers) |
|||
{ |
|||
foreach (var container in containers) |
|||
{ |
|||
var item = _containerToItem[container.ContainerControl]; |
|||
_containerToItem.Remove(container.ContainerControl); |
|||
RemoveContainer(container.ContainerControl, item); |
|||
} |
|||
|
|||
Dematerialized?.Invoke( |
|||
this, |
|||
new ItemContainerEventArgs(startingIndex, containers.ToList())); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the container for an item.
|
|||
/// </summary>
|
|||
/// <param name="item">The item.</param>
|
|||
/// <returns>The container, or null of not found.</returns>
|
|||
public Control? ContainerFromItem(object item) |
|||
{ |
|||
if (item != null) |
|||
{ |
|||
_itemToContainer.TryGetValue(item, out var result); |
|||
if (result == null) |
|||
{ |
|||
_itemToContainerSet.TryGetValue(item, out var set); |
|||
if (set?.Count > 0) |
|||
{ |
|||
return set.FirstOrDefault(); |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the item for a container.
|
|||
/// </summary>
|
|||
/// <param name="container">The container.</param>
|
|||
/// <returns>The item, or null of not found.</returns>
|
|||
public object? ItemFromContainer(Control? container) |
|||
{ |
|||
if (container != null) |
|||
{ |
|||
_containerToItem.TryGetValue(container, out var result); |
|||
if (result != null) |
|||
{ |
|||
_itemToContainer[result] = container; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,164 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Avalonia.Controls.Templates; |
|||
using Avalonia.Data; |
|||
using Avalonia.LogicalTree; |
|||
|
|||
namespace Avalonia.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Creates containers for tree items and maintains a list of created containers.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the container.</typeparam>
|
|||
public class TreeItemContainerGenerator<T> : ItemContainerGenerator<T>, ITreeItemContainerGenerator |
|||
where T : Control, new() |
|||
{ |
|||
private TreeView? _treeView; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="TreeItemContainerGenerator{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="owner">The owner control.</param>
|
|||
/// <param name="contentProperty">The container's Content property.</param>
|
|||
/// <param name="contentTemplateProperty">The container's ContentTemplate property.</param>
|
|||
/// <param name="itemsProperty">The container's Items property.</param>
|
|||
/// <param name="isExpandedProperty">The container's IsExpanded property.</param>
|
|||
public TreeItemContainerGenerator( |
|||
Control owner, |
|||
AvaloniaProperty contentProperty, |
|||
AvaloniaProperty contentTemplateProperty, |
|||
AvaloniaProperty itemsProperty, |
|||
AvaloniaProperty isExpandedProperty) |
|||
: base(owner, contentProperty, contentTemplateProperty) |
|||
{ |
|||
ItemsProperty = itemsProperty ?? throw new ArgumentNullException(nameof(itemsProperty)); |
|||
IsExpandedProperty = isExpandedProperty ?? throw new ArgumentNullException(nameof(isExpandedProperty)); |
|||
UpdateIndex(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the container index for the tree.
|
|||
/// </summary>
|
|||
public TreeContainerIndex? Index { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the item container's Items property.
|
|||
/// </summary>
|
|||
protected AvaloniaProperty ItemsProperty { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the item container's IsExpanded property.
|
|||
/// </summary>
|
|||
protected AvaloniaProperty IsExpandedProperty { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override Control? CreateContainer(object? item) |
|||
{ |
|||
var container = item as T; |
|||
|
|||
if (item == null) |
|||
{ |
|||
return null; |
|||
} |
|||
else if (container != null) |
|||
{ |
|||
Index?.Add(item, container); |
|||
return container; |
|||
} |
|||
else |
|||
{ |
|||
var template = GetTreeDataTemplate(item, ItemTemplate); |
|||
var result = new T(); |
|||
|
|||
if (ItemContainerTheme != null) |
|||
{ |
|||
result.SetValue(Control.ThemeProperty, ItemContainerTheme, BindingPriority.Style); |
|||
} |
|||
|
|||
if (DisplayMemberBinding is not null) |
|||
{ |
|||
result.SetValue(StyledElement.DataContextProperty, item, BindingPriority.Style); |
|||
result.Bind(ContentProperty, DisplayMemberBinding, BindingPriority.Style); |
|||
} |
|||
else |
|||
{ |
|||
result.SetValue(ContentProperty, template.Build(item), BindingPriority.Style); |
|||
} |
|||
|
|||
var itemsSelector = template.ItemsSelector(item); |
|||
|
|||
if (itemsSelector != null) |
|||
{ |
|||
BindingOperations.Apply(result, ItemsProperty, itemsSelector, null); |
|||
} |
|||
|
|||
if (!(item is Control)) |
|||
{ |
|||
result.DataContext = item; |
|||
} |
|||
|
|||
Index?.Add(item, result); |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public override IEnumerable<ItemContainerInfo> Clear() |
|||
{ |
|||
var items = base.Clear(); |
|||
Index?.Remove(0, items); |
|||
return items; |
|||
} |
|||
|
|||
public override IEnumerable<ItemContainerInfo> Dematerialize(int startingIndex, int count) |
|||
{ |
|||
Index?.Remove(startingIndex, GetContainerRange(startingIndex, count)); |
|||
return base.Dematerialize(startingIndex, count); |
|||
} |
|||
|
|||
public override IEnumerable<ItemContainerInfo> RemoveRange(int startingIndex, int count) |
|||
{ |
|||
Index?.Remove(startingIndex, GetContainerRange(startingIndex, count)); |
|||
return base.RemoveRange(startingIndex, count); |
|||
} |
|||
|
|||
public override bool TryRecycle(int oldIndex, int newIndex, object item) => false; |
|||
|
|||
public void UpdateIndex() |
|||
{ |
|||
if (Owner is TreeView treeViewOwner && Index == null) |
|||
{ |
|||
Index = new TreeContainerIndex(); |
|||
_treeView = treeViewOwner; |
|||
} |
|||
else |
|||
{ |
|||
var treeView = Owner.GetSelfAndLogicalAncestors().OfType<TreeView>().FirstOrDefault(); |
|||
|
|||
if (treeView != _treeView) |
|||
{ |
|||
Clear(); |
|||
Index = treeView?.ItemContainerGenerator?.Index; |
|||
_treeView = treeView; |
|||
} |
|||
} |
|||
} |
|||
|
|||
class WrapperTreeDataTemplate : ITreeDataTemplate |
|||
{ |
|||
private readonly IDataTemplate _inner; |
|||
public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner; |
|||
public Control? Build(object? param) => _inner.Build(param); |
|||
public bool Match(object? data) => _inner.Match(data); |
|||
public InstancedBinding? ItemsSelector(object item) => null; |
|||
} |
|||
|
|||
private ITreeDataTemplate GetTreeDataTemplate(object item, IDataTemplate? primary) |
|||
{ |
|||
var template = Owner.FindDataTemplate(item, primary) ?? FuncDataTemplate.Default; |
|||
var treeTemplate = template as ITreeDataTemplate ?? new WrapperTreeDataTemplate(template); |
|||
return treeTemplate; |
|||
} |
|||
} |
|||
} |
|||
@ -1,341 +1,341 @@ |
|||
using System.Collections.ObjectModel; |
|||
using System.Linq; |
|||
using System.Reactive.Subjects; |
|||
using Avalonia.Controls.Presenters; |
|||
using Avalonia.Controls.Templates; |
|||
using Avalonia.Data; |
|||
using Avalonia.LogicalTree; |
|||
using Avalonia.UnitTests; |
|||
using Avalonia.VisualTree; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Controls.UnitTests |
|||
{ |
|||
public class CarouselTests |
|||
{ |
|||
[Fact] |
|||
public void First_Item_Should_Be_Selected_By_Default() |
|||
{ |
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = new[] |
|||
{ |
|||
"Foo", |
|||
"Bar" |
|||
} |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
|
|||
Assert.Equal(0, target.SelectedIndex); |
|||
Assert.Equal("Foo", target.SelectedItem); |
|||
} |
|||
|
|||
[Fact] |
|||
public void LogicalChild_Should_Be_Selected_Item() |
|||
{ |
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = new[] |
|||
{ |
|||
"Foo", |
|||
"Bar" |
|||
} |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
|
|||
Assert.Single(target.GetLogicalChildren()); |
|||
|
|||
var child = GetContainerTextBlock(target.GetLogicalChildren().Single()); |
|||
|
|||
Assert.Equal("Foo", child.Text); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Remove_NonCurrent_Page_When_IsVirtualized_True() |
|||
{ |
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = new[] { "foo", "bar" }, |
|||
IsVirtualized = true, |
|||
SelectedIndex = 0, |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
|
|||
Assert.Single(target.ItemContainerGenerator.Containers); |
|||
target.SelectedIndex = 1; |
|||
Assert.Single(target.ItemContainerGenerator.Containers); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes() |
|||
{ |
|||
var items = new ObservableCollection<string> |
|||
{ |
|||
"Foo", |
|||
"Bar", |
|||
"FooBar" |
|||
}; |
|||
|
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = items, |
|||
IsVirtualized = false |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
|
|||
Assert.Equal(3, target.GetLogicalChildren().Count()); |
|||
|
|||
var child = GetContainerTextBlock(target.GetLogicalChildren().First()); |
|||
|
|||
Assert.Equal("Foo", child.Text); |
|||
|
|||
var newItems = items.ToList(); |
|||
newItems.RemoveAt(0); |
|||
|
|||
target.Items = newItems; |
|||
|
|||
child = GetContainerTextBlock(target.GetLogicalChildren().First()); |
|||
|
|||
Assert.Equal("Bar", child.Text); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes_And_Virtualized() |
|||
{ |
|||
var items = new ObservableCollection<string> |
|||
{ |
|||
"Foo", |
|||
"Bar", |
|||
"FooBar" |
|||
}; |
|||
|
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = items, |
|||
IsVirtualized = true, |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
|
|||
Assert.Single(target.GetLogicalChildren()); |
|||
|
|||
var child = GetContainerTextBlock(target.GetLogicalChildren().Single()); |
|||
|
|||
Assert.Equal("Foo", child.Text); |
|||
|
|||
var newItems = items.ToList(); |
|||
newItems.RemoveAt(0); |
|||
|
|||
target.Items = newItems; |
|||
|
|||
child = GetContainerTextBlock(target.GetLogicalChildren().Single()); |
|||
|
|||
Assert.Equal("Bar", child.Text); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Selected_Item_Changes_To_First_Item_When_Item_Added() |
|||
{ |
|||
var items = new ObservableCollection<string>(); |
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = items, |
|||
IsVirtualized = false |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
////using System.Collections.ObjectModel;
|
|||
////using System.Linq;
|
|||
////using System.Reactive.Subjects;
|
|||
////using Avalonia.Controls.Presenters;
|
|||
////using Avalonia.Controls.Templates;
|
|||
////using Avalonia.Data;
|
|||
////using Avalonia.LogicalTree;
|
|||
////using Avalonia.UnitTests;
|
|||
////using Avalonia.VisualTree;
|
|||
////using Xunit;
|
|||
|
|||
////namespace Avalonia.Controls.UnitTests
|
|||
////{
|
|||
//// public class CarouselTests
|
|||
//// {
|
|||
//// [Fact]
|
|||
//// public void First_Item_Should_Be_Selected_By_Default()
|
|||
//// {
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = new[]
|
|||
//// {
|
|||
//// "Foo",
|
|||
//// "Bar"
|
|||
//// }
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
|
|||
//// Assert.Equal(0, target.SelectedIndex);
|
|||
//// Assert.Equal("Foo", target.SelectedItem);
|
|||
//// }
|
|||
|
|||
//// [Fact]
|
|||
//// public void LogicalChild_Should_Be_Selected_Item()
|
|||
//// {
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = new[]
|
|||
//// {
|
|||
//// "Foo",
|
|||
//// "Bar"
|
|||
//// }
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
//// Assert.Single(target.GetLogicalChildren());
|
|||
|
|||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().Single());
|
|||
|
|||
//// Assert.Equal("Foo", child.Text);
|
|||
//// }
|
|||
|
|||
//// [Fact]
|
|||
//// public void Should_Remove_NonCurrent_Page_When_IsVirtualized_True()
|
|||
//// {
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = new[] { "foo", "bar" },
|
|||
//// IsVirtualized = true,
|
|||
//// SelectedIndex = 0,
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
//// Assert.Single(target.ItemContainerGenerator.Containers);
|
|||
//// target.SelectedIndex = 1;
|
|||
//// Assert.Single(target.ItemContainerGenerator.Containers);
|
|||
//// }
|
|||
|
|||
//// [Fact]
|
|||
//// public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes()
|
|||
//// {
|
|||
//// var items = new ObservableCollection<string>
|
|||
//// {
|
|||
//// "Foo",
|
|||
//// "Bar",
|
|||
//// "FooBar"
|
|||
//// };
|
|||
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = items,
|
|||
//// IsVirtualized = false
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
//// Assert.Equal(3, target.GetLogicalChildren().Count());
|
|||
|
|||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
|||
|
|||
//// Assert.Equal("Foo", child.Text);
|
|||
|
|||
//// var newItems = items.ToList();
|
|||
//// newItems.RemoveAt(0);
|
|||
|
|||
//// target.Items = newItems;
|
|||
|
|||
//// child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
|||
|
|||
//// Assert.Equal("Bar", child.Text);
|
|||
//// }
|
|||
|
|||
//// [Fact]
|
|||
//// public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes_And_Virtualized()
|
|||
//// {
|
|||
//// var items = new ObservableCollection<string>
|
|||
//// {
|
|||
//// "Foo",
|
|||
//// "Bar",
|
|||
//// "FooBar"
|
|||
//// };
|
|||
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = items,
|
|||
//// IsVirtualized = true,
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
//// Assert.Single(target.GetLogicalChildren());
|
|||
|
|||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().Single());
|
|||
|
|||
//// Assert.Equal("Foo", child.Text);
|
|||
|
|||
//// var newItems = items.ToList();
|
|||
//// newItems.RemoveAt(0);
|
|||
|
|||
//// target.Items = newItems;
|
|||
|
|||
//// child = GetContainerTextBlock(target.GetLogicalChildren().Single());
|
|||
|
|||
//// Assert.Equal("Bar", child.Text);
|
|||
//// }
|
|||
|
|||
//// [Fact]
|
|||
//// public void Selected_Item_Changes_To_First_Item_When_Item_Added()
|
|||
//// {
|
|||
//// var items = new ObservableCollection<string>();
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = items,
|
|||
//// IsVirtualized = false
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
Assert.Equal(-1, target.SelectedIndex); |
|||
Assert.Empty(target.GetLogicalChildren()); |
|||
//// Assert.Equal(-1, target.SelectedIndex);
|
|||
//// Assert.Empty(target.GetLogicalChildren());
|
|||
|
|||
items.Add("Foo"); |
|||
//// items.Add("Foo");
|
|||
|
|||
Assert.Equal(0, target.SelectedIndex); |
|||
Assert.Single(target.GetLogicalChildren()); |
|||
} |
|||
//// Assert.Equal(0, target.SelectedIndex);
|
|||
//// Assert.Single(target.GetLogicalChildren());
|
|||
//// }
|
|||
|
|||
[Fact] |
|||
public void Selected_Index_Changes_To_None_When_Items_Assigned_Null() |
|||
{ |
|||
var items = new ObservableCollection<string> |
|||
{ |
|||
"Foo", |
|||
"Bar", |
|||
"FooBar" |
|||
}; |
|||
//// [Fact]
|
|||
//// public void Selected_Index_Changes_To_None_When_Items_Assigned_Null()
|
|||
//// {
|
|||
//// var items = new ObservableCollection<string>
|
|||
//// {
|
|||
//// "Foo",
|
|||
//// "Bar",
|
|||
//// "FooBar"
|
|||
//// };
|
|||
|
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = items, |
|||
IsVirtualized = false |
|||
}; |
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = items,
|
|||
//// IsVirtualized = false
|
|||
//// };
|
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
Assert.Equal(3, target.GetLogicalChildren().Count()); |
|||
//// Assert.Equal(3, target.GetLogicalChildren().Count());
|
|||
|
|||
var child = GetContainerTextBlock(target.GetLogicalChildren().First()); |
|||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
|||
|
|||
Assert.Equal("Foo", child.Text); |
|||
//// Assert.Equal("Foo", child.Text);
|
|||
|
|||
target.Items = null; |
|||
//// target.Items = null;
|
|||
|
|||
var numChildren = target.GetLogicalChildren().Count(); |
|||
//// var numChildren = target.GetLogicalChildren().Count();
|
|||
|
|||
Assert.Equal(0, numChildren); |
|||
Assert.Equal(-1, target.SelectedIndex); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Selected_Index_Is_Maintained_Carousel_Created_With_Non_Zero_SelectedIndex() |
|||
{ |
|||
var items = new ObservableCollection<string> |
|||
{ |
|||
"Foo", |
|||
"Bar", |
|||
"FooBar" |
|||
}; |
|||
|
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = items, |
|||
IsVirtualized = false, |
|||
SelectedIndex = 2 |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
|
|||
Assert.Equal("FooBar", target.SelectedItem); |
|||
|
|||
var child = GetContainerTextBlock(target.GetVisualDescendants().LastOrDefault()); |
|||
|
|||
Assert.IsType<TextBlock>(child); |
|||
Assert.Equal("FooBar", ((TextBlock)child).Text); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Selected_Item_Changes_To_Next_First_Item_When_Item_Removed_From_Beggining_Of_List() |
|||
{ |
|||
var items = new ObservableCollection<string> |
|||
{ |
|||
"Foo", |
|||
"Bar", |
|||
"FooBar" |
|||
}; |
|||
|
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = items, |
|||
IsVirtualized = false |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
|
|||
Assert.Equal(3, target.GetLogicalChildren().Count()); |
|||
|
|||
var child = GetContainerTextBlock(target.GetLogicalChildren().First()); |
|||
|
|||
Assert.Equal("Foo", child.Text); |
|||
|
|||
items.RemoveAt(0); |
|||
|
|||
child = GetContainerTextBlock(target.GetLogicalChildren().First()); |
|||
|
|||
Assert.IsType<TextBlock>(child); |
|||
Assert.Equal("Bar", ((TextBlock)child).Text); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Selected_Item_Changes_To_First_Item_If_SelectedItem_Is_Removed_From_Middle() |
|||
{ |
|||
var items = new ObservableCollection<string> |
|||
{ |
|||
"Foo", |
|||
"Bar", |
|||
"FooBar" |
|||
}; |
|||
|
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
Items = items, |
|||
IsVirtualized = false |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
|
|||
target.SelectedIndex = 1; |
|||
|
|||
items.RemoveAt(1); |
|||
|
|||
Assert.Equal(0, target.SelectedIndex); |
|||
Assert.Equal("Foo", target.SelectedItem); |
|||
} |
|||
|
|||
private Control CreateTemplate(Carousel control, INameScope scope) |
|||
{ |
|||
return new CarouselPresenter |
|||
{ |
|||
Name = "PART_ItemsPresenter", |
|||
[~CarouselPresenter.IsVirtualizedProperty] = control[~Carousel.IsVirtualizedProperty], |
|||
[~CarouselPresenter.ItemsPanelProperty] = control[~Carousel.ItemsPanelProperty], |
|||
[~CarouselPresenter.SelectedIndexProperty] = control[~Carousel.SelectedIndexProperty], |
|||
[~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty], |
|||
}.RegisterInNameScope(scope); |
|||
} |
|||
|
|||
private static TextBlock GetContainerTextBlock(object control) |
|||
{ |
|||
var contentPresenter = Assert.IsType<ContentPresenter>(control); |
|||
contentPresenter.UpdateChild(); |
|||
return Assert.IsType<TextBlock>(contentPresenter.Child); |
|||
} |
|||
|
|||
[Fact] |
|||
public void SelectedItem_Validation() |
|||
{ |
|||
using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) |
|||
{ |
|||
var target = new Carousel |
|||
{ |
|||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
|||
IsVirtualized = false |
|||
}; |
|||
|
|||
target.ApplyTemplate(); |
|||
((Control)target.Presenter).ApplyTemplate(); |
|||
|
|||
var exception = new System.InvalidCastException("failed validation"); |
|||
var textObservable = |
|||
new BehaviorSubject<BindingNotification>(new BindingNotification(exception, |
|||
BindingErrorType.DataValidationError)); |
|||
target.Bind(ComboBox.SelectedItemProperty, textObservable); |
|||
|
|||
Assert.True(DataValidationErrors.GetHasErrors(target)); |
|||
Assert.True(DataValidationErrors.GetErrors(target).SequenceEqual(new[] { exception })); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
//// Assert.Equal(0, numChildren);
|
|||
//// Assert.Equal(-1, target.SelectedIndex);
|
|||
//// }
|
|||
|
|||
//// [Fact]
|
|||
//// public void Selected_Index_Is_Maintained_Carousel_Created_With_Non_Zero_SelectedIndex()
|
|||
//// {
|
|||
//// var items = new ObservableCollection<string>
|
|||
//// {
|
|||
//// "Foo",
|
|||
//// "Bar",
|
|||
//// "FooBar"
|
|||
//// };
|
|||
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = items,
|
|||
//// IsVirtualized = false,
|
|||
//// SelectedIndex = 2
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
//// Assert.Equal("FooBar", target.SelectedItem);
|
|||
|
|||
//// var child = GetContainerTextBlock(target.GetVisualDescendants().LastOrDefault());
|
|||
|
|||
//// Assert.IsType<TextBlock>(child);
|
|||
//// Assert.Equal("FooBar", ((TextBlock)child).Text);
|
|||
//// }
|
|||
|
|||
//// [Fact]
|
|||
//// public void Selected_Item_Changes_To_Next_First_Item_When_Item_Removed_From_Beggining_Of_List()
|
|||
//// {
|
|||
//// var items = new ObservableCollection<string>
|
|||
//// {
|
|||
//// "Foo",
|
|||
//// "Bar",
|
|||
//// "FooBar"
|
|||
//// };
|
|||
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = items,
|
|||
//// IsVirtualized = false
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
//// Assert.Equal(3, target.GetLogicalChildren().Count());
|
|||
|
|||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
|||
|
|||
//// Assert.Equal("Foo", child.Text);
|
|||
|
|||
//// items.RemoveAt(0);
|
|||
|
|||
//// child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
|||
|
|||
//// Assert.IsType<TextBlock>(child);
|
|||
//// Assert.Equal("Bar", ((TextBlock)child).Text);
|
|||
//// }
|
|||
|
|||
//// [Fact]
|
|||
//// public void Selected_Item_Changes_To_First_Item_If_SelectedItem_Is_Removed_From_Middle()
|
|||
//// {
|
|||
//// var items = new ObservableCollection<string>
|
|||
//// {
|
|||
//// "Foo",
|
|||
//// "Bar",
|
|||
//// "FooBar"
|
|||
//// };
|
|||
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// Items = items,
|
|||
//// IsVirtualized = false
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
//// target.SelectedIndex = 1;
|
|||
|
|||
//// items.RemoveAt(1);
|
|||
|
|||
//// Assert.Equal(0, target.SelectedIndex);
|
|||
//// Assert.Equal("Foo", target.SelectedItem);
|
|||
//// }
|
|||
|
|||
//// private Control CreateTemplate(Carousel control, INameScope scope)
|
|||
//// {
|
|||
//// return new CarouselPresenter
|
|||
//// {
|
|||
//// Name = "PART_ItemsPresenter",
|
|||
//// [~CarouselPresenter.IsVirtualizedProperty] = control[~Carousel.IsVirtualizedProperty],
|
|||
//// [~CarouselPresenter.ItemsPanelProperty] = control[~Carousel.ItemsPanelProperty],
|
|||
//// [~CarouselPresenter.SelectedIndexProperty] = control[~Carousel.SelectedIndexProperty],
|
|||
//// [~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty],
|
|||
//// }.RegisterInNameScope(scope);
|
|||
//// }
|
|||
|
|||
//// private static TextBlock GetContainerTextBlock(object control)
|
|||
//// {
|
|||
//// var contentPresenter = Assert.IsType<ContentPresenter>(control);
|
|||
//// contentPresenter.UpdateChild();
|
|||
//// return Assert.IsType<TextBlock>(contentPresenter.Child);
|
|||
//// }
|
|||
|
|||
//// [Fact]
|
|||
//// public void SelectedItem_Validation()
|
|||
//// {
|
|||
//// using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
|
|||
//// {
|
|||
//// var target = new Carousel
|
|||
//// {
|
|||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|||
//// IsVirtualized = false
|
|||
//// };
|
|||
|
|||
//// target.ApplyTemplate();
|
|||
//// ((Control)target.Presenter).ApplyTemplate();
|
|||
|
|||
//// var exception = new System.InvalidCastException("failed validation");
|
|||
//// var textObservable =
|
|||
//// new BehaviorSubject<BindingNotification>(new BindingNotification(exception,
|
|||
//// BindingErrorType.DataValidationError));
|
|||
//// target.Bind(ComboBox.SelectedItemProperty, textObservable);
|
|||
|
|||
//// Assert.True(DataValidationErrors.GetHasErrors(target));
|
|||
//// Assert.True(DataValidationErrors.GetErrors(target).SequenceEqual(new[] { exception }));
|
|||
//// }
|
|||
//// }
|
|||
//// }
|
|||
////}
|
|||
|
|||
@ -1,173 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reactive.Linq; |
|||
using Avalonia.Controls.Generators; |
|||
using Avalonia.Controls.Presenters; |
|||
using Avalonia.Data; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Controls.UnitTests.Generators |
|||
{ |
|||
public class ItemContainerGeneratorTests |
|||
{ |
|||
[Fact] |
|||
public void Materialize_Should_Create_Containers() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = Materialize(target, 0, items); |
|||
var result = containers |
|||
.Select(x => x.ContainerControl) |
|||
.OfType<ContentPresenter>() |
|||
.Select(x => x.Content) |
|||
.ToList(); |
|||
|
|||
Assert.Equal(items, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ContainerFromIndex_Should_Return_Materialized_Containers() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = Materialize(target, 0, items); |
|||
|
|||
Assert.Equal(containers[0].ContainerControl, target.ContainerFromIndex(0)); |
|||
Assert.Equal(containers[1].ContainerControl, target.ContainerFromIndex(1)); |
|||
Assert.Equal(containers[2].ContainerControl, target.ContainerFromIndex(2)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void IndexFromContainer_Should_Return_Index() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = Materialize(target, 0, items); |
|||
|
|||
Assert.Equal(0, target.IndexFromContainer(containers[0].ContainerControl)); |
|||
Assert.Equal(1, target.IndexFromContainer(containers[1].ContainerControl)); |
|||
Assert.Equal(2, target.IndexFromContainer(containers[2].ContainerControl)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dematerialize_Should_Remove_Container() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = Materialize(target, 0, items); |
|||
|
|||
target.Dematerialize(1, 1); |
|||
|
|||
Assert.Equal(containers[0].ContainerControl, target.ContainerFromIndex(0)); |
|||
Assert.Null(target.ContainerFromIndex(1)); |
|||
Assert.Equal(containers[2].ContainerControl, target.ContainerFromIndex(2)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dematerialize_Should_Return_Removed_Containers() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = Materialize(target, 0, items); |
|||
var expected = target.Containers.Take(2).ToList(); |
|||
var result = target.Dematerialize(0, 2); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void InsertSpace_Should_Alter_Successive_Container_Indexes() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = Materialize(target, 0, items); |
|||
|
|||
target.InsertSpace(1, 3); |
|||
|
|||
Assert.Equal(3, target.Containers.Count()); |
|||
Assert.Equal(new[] { 0, 4, 5 }, target.Containers.Select(x => x.Index)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveRange_Should_Alter_Successive_Container_Indexes() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = Materialize(target, 0, items); |
|||
|
|||
var removed = target.RemoveRange(1, 1).Single(); |
|||
|
|||
Assert.Equal(containers[0].ContainerControl, target.ContainerFromIndex(0)); |
|||
Assert.Equal(containers[2].ContainerControl, target.ContainerFromIndex(1)); |
|||
Assert.Equal(containers[1], removed); |
|||
Assert.Equal(new[] { 0, 1 }, target.Containers.Select(x => x.Index)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Style_Binding_Should_Be_Able_To_Override_Content() |
|||
{ |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var container = (ContentPresenter)target.Materialize(0, "foo").ContainerControl; |
|||
|
|||
Assert.Equal("foo", container.Content); |
|||
|
|||
container.Bind( |
|||
ContentPresenter.ContentProperty, |
|||
Observable.Never<object>().StartWith("bar"), |
|||
BindingPriority.Style); |
|||
|
|||
Assert.Equal("bar", container.Content); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Style_Binding_Should_Be_Able_To_Override_Content_Typed() |
|||
{ |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator<ListBoxItem>(owner, ListBoxItem.ContentProperty, null); |
|||
var container = (ListBoxItem)target.Materialize(0, "foo").ContainerControl; |
|||
|
|||
Assert.Equal("foo", container.Content); |
|||
|
|||
container.Bind( |
|||
ContentPresenter.ContentProperty, |
|||
Observable.Never<object>().StartWith("bar"), |
|||
BindingPriority.Style); |
|||
|
|||
Assert.Equal("bar", container.Content); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Materialize_Should_Create_Containers_When_Item_Is_Null() |
|||
{ |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator<ListBoxItem>(owner, ListBoxItem.ContentProperty, null); |
|||
var container = (ListBoxItem)target.Materialize(0, null).ContainerControl; |
|||
|
|||
Assert.True(container != null, "The containers is not materialized."); |
|||
} |
|||
|
|||
private static IList<ItemContainerInfo> Materialize( |
|||
IItemContainerGenerator generator, |
|||
int index, |
|||
string[] items) |
|||
{ |
|||
var result = new List<ItemContainerInfo>(); |
|||
|
|||
foreach (var item in items) |
|||
{ |
|||
var container = generator.Materialize(index++, item); |
|||
result.Add(container); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -1,42 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Avalonia.Controls.Generators; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Controls.UnitTests.Generators |
|||
{ |
|||
public class ItemContainerGeneratorTypedTests |
|||
{ |
|||
[Fact] |
|||
public void Materialize_Should_Create_Containers() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator<ListBoxItem>(owner, ListBoxItem.ContentProperty, null); |
|||
var containers = Materialize(target, 0, items); |
|||
var result = containers |
|||
.Select(x => x.ContainerControl) |
|||
.OfType<ListBoxItem>() |
|||
.Select(x => x.Content) |
|||
.ToList(); |
|||
|
|||
Assert.Equal(items, result); |
|||
} |
|||
|
|||
private static IList<ItemContainerInfo> Materialize( |
|||
IItemContainerGenerator generator, |
|||
int index, |
|||
string[] items) |
|||
{ |
|||
var result = new List<ItemContainerInfo>(); |
|||
|
|||
foreach (var item in items) |
|||
{ |
|||
var container = generator.Materialize(index++, item); |
|||
result.Add(container); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue