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; |
||||
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 |
namespace Avalonia.Controls.Generators |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Creates containers for items and maintains a list of created containers.
|
/// Generates containers for an <see cref="ItemsControl"/>.
|
||||
/// </summary>
|
/// </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>
|
internal ItemContainerGenerator(ItemsControl owner) => _owner = owner; |
||||
/// 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; |
|
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Gets or sets the theme to be applied to the items in the control.
|
/// Creates a new container control.
|
||||
/// </summary>
|
/// </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>
|
/// <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>
|
/// </summary>
|
||||
public IDataTemplate? ItemTemplate { get; set; } |
/// <param name="container">The item.</param>
|
||||
|
/// <returns>true if the item is its own container, otherwise false.</returns>
|
||||
/// <inheritdoc />
|
/// <remarks>
|
||||
public IBinding? DisplayMemberBinding { get; set; } |
/// 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>
|
/// <summary>
|
||||
/// Gets the owner control.
|
/// Prepares the specified element as the container for the corresponding item.
|
||||
/// </summary>
|
/// </summary>
|
||||
public Control Owner { get; } |
/// <param name="container">The element that's used to display the specified item.</param>
|
||||
|
/// <param name="item">The item to display.</param>
|
||||
/// <inheritdoc/>
|
/// <param name="index">The index of the item to display.</param>
|
||||
public virtual Type? ContainerType => null; |
/// <remarks>
|
||||
|
/// If <see cref="IsItemItsOwnContainer(Control)"/> is true for an item, then this method
|
||||
/// <inheritdoc/>
|
/// only needs to be called a single time, otherwise this method should be called after the
|
||||
public ItemContainerInfo Materialize(int index, object item) |
/// container is created, and each subsequent time the container is recycled to display a
|
||||
{ |
/// new item.
|
||||
var container = new ItemContainerInfo(CreateContainer(item)!, item, index); |
/// </remarks>
|
||||
|
public void PrepareItemContainer(Control container, object? item, int index) => |
||||
_containers.Add(container.Index, container); |
_owner.PrepareItemContainer(container, item, index); |
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Creates the container for an item.
|
/// Undoes the effects of the <see cref="PrepareItemContainer(Control, object, int)"/> method.
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
/// <param name="container">The element that's used to display the specified item.</param>
|
||||
/// <returns>The created container control.</returns>
|
public void ClearItemContainer(Control container) => _owner.ClearContainerForItemOverride(container); |
||||
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); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (ItemContainerTheme != null) |
public Control? ContainerFromIndex(int index) => _owner.ContainerFromIndex(index); |
||||
{ |
public int IndexFromContainer(Control container) => _owner.IndexFromContainer(container); |
||||
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); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -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.Collections.ObjectModel;
|
||||
using System.Linq; |
////using System.Linq;
|
||||
using System.Reactive.Subjects; |
////using System.Reactive.Subjects;
|
||||
using Avalonia.Controls.Presenters; |
////using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Templates; |
////using Avalonia.Controls.Templates;
|
||||
using Avalonia.Data; |
////using Avalonia.Data;
|
||||
using Avalonia.LogicalTree; |
////using Avalonia.LogicalTree;
|
||||
using Avalonia.UnitTests; |
////using Avalonia.UnitTests;
|
||||
using Avalonia.VisualTree; |
////using Avalonia.VisualTree;
|
||||
using Xunit; |
////using Xunit;
|
||||
|
|
||||
namespace Avalonia.Controls.UnitTests |
////namespace Avalonia.Controls.UnitTests
|
||||
{ |
////{
|
||||
public class CarouselTests |
//// public class CarouselTests
|
||||
{ |
//// {
|
||||
[Fact] |
//// [Fact]
|
||||
public void First_Item_Should_Be_Selected_By_Default() |
//// public void First_Item_Should_Be_Selected_By_Default()
|
||||
{ |
//// {
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = new[] |
//// Items = new[]
|
||||
{ |
//// {
|
||||
"Foo", |
//// "Foo",
|
||||
"Bar" |
//// "Bar"
|
||||
} |
//// }
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
|
|
||||
Assert.Equal(0, target.SelectedIndex); |
//// Assert.Equal(0, target.SelectedIndex);
|
||||
Assert.Equal("Foo", target.SelectedItem); |
//// Assert.Equal("Foo", target.SelectedItem);
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void LogicalChild_Should_Be_Selected_Item() |
//// public void LogicalChild_Should_Be_Selected_Item()
|
||||
{ |
//// {
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = new[] |
//// Items = new[]
|
||||
{ |
//// {
|
||||
"Foo", |
//// "Foo",
|
||||
"Bar" |
//// "Bar"
|
||||
} |
//// }
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).ApplyTemplate(); |
//// ((Control)target.Presenter).ApplyTemplate();
|
||||
|
|
||||
Assert.Single(target.GetLogicalChildren()); |
//// Assert.Single(target.GetLogicalChildren());
|
||||
|
|
||||
var child = GetContainerTextBlock(target.GetLogicalChildren().Single()); |
//// var child = GetContainerTextBlock(target.GetLogicalChildren().Single());
|
||||
|
|
||||
Assert.Equal("Foo", child.Text); |
//// Assert.Equal("Foo", child.Text);
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void Should_Remove_NonCurrent_Page_When_IsVirtualized_True() |
//// public void Should_Remove_NonCurrent_Page_When_IsVirtualized_True()
|
||||
{ |
//// {
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = new[] { "foo", "bar" }, |
//// Items = new[] { "foo", "bar" },
|
||||
IsVirtualized = true, |
//// IsVirtualized = true,
|
||||
SelectedIndex = 0, |
//// SelectedIndex = 0,
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).ApplyTemplate(); |
//// ((Control)target.Presenter).ApplyTemplate();
|
||||
|
|
||||
Assert.Single(target.ItemContainerGenerator.Containers); |
//// Assert.Single(target.ItemContainerGenerator.Containers);
|
||||
target.SelectedIndex = 1; |
//// target.SelectedIndex = 1;
|
||||
Assert.Single(target.ItemContainerGenerator.Containers); |
//// Assert.Single(target.ItemContainerGenerator.Containers);
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes() |
//// public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes()
|
||||
{ |
//// {
|
||||
var items = new ObservableCollection<string> |
//// var items = new ObservableCollection<string>
|
||||
{ |
//// {
|
||||
"Foo", |
//// "Foo",
|
||||
"Bar", |
//// "Bar",
|
||||
"FooBar" |
//// "FooBar"
|
||||
}; |
//// };
|
||||
|
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = items, |
//// Items = items,
|
||||
IsVirtualized = false |
//// IsVirtualized = false
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).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);
|
||||
|
|
||||
var newItems = items.ToList(); |
//// var newItems = items.ToList();
|
||||
newItems.RemoveAt(0); |
//// newItems.RemoveAt(0);
|
||||
|
|
||||
target.Items = newItems; |
//// target.Items = newItems;
|
||||
|
|
||||
child = GetContainerTextBlock(target.GetLogicalChildren().First()); |
//// child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
||||
|
|
||||
Assert.Equal("Bar", child.Text); |
//// Assert.Equal("Bar", child.Text);
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes_And_Virtualized() |
//// public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes_And_Virtualized()
|
||||
{ |
//// {
|
||||
var items = new ObservableCollection<string> |
//// var items = new ObservableCollection<string>
|
||||
{ |
//// {
|
||||
"Foo", |
//// "Foo",
|
||||
"Bar", |
//// "Bar",
|
||||
"FooBar" |
//// "FooBar"
|
||||
}; |
//// };
|
||||
|
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = items, |
//// Items = items,
|
||||
IsVirtualized = true, |
//// IsVirtualized = true,
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).ApplyTemplate(); |
//// ((Control)target.Presenter).ApplyTemplate();
|
||||
|
|
||||
Assert.Single(target.GetLogicalChildren()); |
//// Assert.Single(target.GetLogicalChildren());
|
||||
|
|
||||
var child = GetContainerTextBlock(target.GetLogicalChildren().Single()); |
//// var child = GetContainerTextBlock(target.GetLogicalChildren().Single());
|
||||
|
|
||||
Assert.Equal("Foo", child.Text); |
//// Assert.Equal("Foo", child.Text);
|
||||
|
|
||||
var newItems = items.ToList(); |
//// var newItems = items.ToList();
|
||||
newItems.RemoveAt(0); |
//// newItems.RemoveAt(0);
|
||||
|
|
||||
target.Items = newItems; |
//// target.Items = newItems;
|
||||
|
|
||||
child = GetContainerTextBlock(target.GetLogicalChildren().Single()); |
//// child = GetContainerTextBlock(target.GetLogicalChildren().Single());
|
||||
|
|
||||
Assert.Equal("Bar", child.Text); |
//// Assert.Equal("Bar", child.Text);
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void Selected_Item_Changes_To_First_Item_When_Item_Added() |
//// public void Selected_Item_Changes_To_First_Item_When_Item_Added()
|
||||
{ |
//// {
|
||||
var items = new ObservableCollection<string>(); |
//// var items = new ObservableCollection<string>();
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = items, |
//// Items = items,
|
||||
IsVirtualized = false |
//// IsVirtualized = false
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).ApplyTemplate(); |
//// ((Control)target.Presenter).ApplyTemplate();
|
||||
|
|
||||
Assert.Equal(-1, target.SelectedIndex); |
//// Assert.Equal(-1, target.SelectedIndex);
|
||||
Assert.Empty(target.GetLogicalChildren()); |
//// Assert.Empty(target.GetLogicalChildren());
|
||||
|
|
||||
items.Add("Foo"); |
//// items.Add("Foo");
|
||||
|
|
||||
Assert.Equal(0, target.SelectedIndex); |
//// Assert.Equal(0, target.SelectedIndex);
|
||||
Assert.Single(target.GetLogicalChildren()); |
//// Assert.Single(target.GetLogicalChildren());
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void Selected_Index_Changes_To_None_When_Items_Assigned_Null() |
//// public void Selected_Index_Changes_To_None_When_Items_Assigned_Null()
|
||||
{ |
//// {
|
||||
var items = new ObservableCollection<string> |
//// var items = new ObservableCollection<string>
|
||||
{ |
//// {
|
||||
"Foo", |
//// "Foo",
|
||||
"Bar", |
//// "Bar",
|
||||
"FooBar" |
//// "FooBar"
|
||||
}; |
//// };
|
||||
|
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = items, |
//// Items = items,
|
||||
IsVirtualized = false |
//// IsVirtualized = false
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).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(0, numChildren);
|
||||
Assert.Equal(-1, target.SelectedIndex); |
//// Assert.Equal(-1, target.SelectedIndex);
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void Selected_Index_Is_Maintained_Carousel_Created_With_Non_Zero_SelectedIndex() |
//// public void Selected_Index_Is_Maintained_Carousel_Created_With_Non_Zero_SelectedIndex()
|
||||
{ |
//// {
|
||||
var items = new ObservableCollection<string> |
//// var items = new ObservableCollection<string>
|
||||
{ |
//// {
|
||||
"Foo", |
//// "Foo",
|
||||
"Bar", |
//// "Bar",
|
||||
"FooBar" |
//// "FooBar"
|
||||
}; |
//// };
|
||||
|
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = items, |
//// Items = items,
|
||||
IsVirtualized = false, |
//// IsVirtualized = false,
|
||||
SelectedIndex = 2 |
//// SelectedIndex = 2
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).ApplyTemplate(); |
//// ((Control)target.Presenter).ApplyTemplate();
|
||||
|
|
||||
Assert.Equal("FooBar", target.SelectedItem); |
//// Assert.Equal("FooBar", target.SelectedItem);
|
||||
|
|
||||
var child = GetContainerTextBlock(target.GetVisualDescendants().LastOrDefault()); |
//// var child = GetContainerTextBlock(target.GetVisualDescendants().LastOrDefault());
|
||||
|
|
||||
Assert.IsType<TextBlock>(child); |
//// Assert.IsType<TextBlock>(child);
|
||||
Assert.Equal("FooBar", ((TextBlock)child).Text); |
//// Assert.Equal("FooBar", ((TextBlock)child).Text);
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void Selected_Item_Changes_To_Next_First_Item_When_Item_Removed_From_Beggining_Of_List() |
//// public void Selected_Item_Changes_To_Next_First_Item_When_Item_Removed_From_Beggining_Of_List()
|
||||
{ |
//// {
|
||||
var items = new ObservableCollection<string> |
//// var items = new ObservableCollection<string>
|
||||
{ |
//// {
|
||||
"Foo", |
//// "Foo",
|
||||
"Bar", |
//// "Bar",
|
||||
"FooBar" |
//// "FooBar"
|
||||
}; |
//// };
|
||||
|
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = items, |
//// Items = items,
|
||||
IsVirtualized = false |
//// IsVirtualized = false
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).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);
|
||||
|
|
||||
items.RemoveAt(0); |
//// items.RemoveAt(0);
|
||||
|
|
||||
child = GetContainerTextBlock(target.GetLogicalChildren().First()); |
//// child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
||||
|
|
||||
Assert.IsType<TextBlock>(child); |
//// Assert.IsType<TextBlock>(child);
|
||||
Assert.Equal("Bar", ((TextBlock)child).Text); |
//// Assert.Equal("Bar", ((TextBlock)child).Text);
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void Selected_Item_Changes_To_First_Item_If_SelectedItem_Is_Removed_From_Middle() |
//// public void Selected_Item_Changes_To_First_Item_If_SelectedItem_Is_Removed_From_Middle()
|
||||
{ |
//// {
|
||||
var items = new ObservableCollection<string> |
//// var items = new ObservableCollection<string>
|
||||
{ |
//// {
|
||||
"Foo", |
//// "Foo",
|
||||
"Bar", |
//// "Bar",
|
||||
"FooBar" |
//// "FooBar"
|
||||
}; |
//// };
|
||||
|
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
Items = items, |
//// Items = items,
|
||||
IsVirtualized = false |
//// IsVirtualized = false
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).ApplyTemplate(); |
//// ((Control)target.Presenter).ApplyTemplate();
|
||||
|
|
||||
target.SelectedIndex = 1; |
//// target.SelectedIndex = 1;
|
||||
|
|
||||
items.RemoveAt(1); |
//// items.RemoveAt(1);
|
||||
|
|
||||
Assert.Equal(0, target.SelectedIndex); |
//// Assert.Equal(0, target.SelectedIndex);
|
||||
Assert.Equal("Foo", target.SelectedItem); |
//// Assert.Equal("Foo", target.SelectedItem);
|
||||
} |
//// }
|
||||
|
|
||||
private Control CreateTemplate(Carousel control, INameScope scope) |
//// private Control CreateTemplate(Carousel control, INameScope scope)
|
||||
{ |
//// {
|
||||
return new CarouselPresenter |
//// return new CarouselPresenter
|
||||
{ |
//// {
|
||||
Name = "PART_ItemsPresenter", |
//// Name = "PART_ItemsPresenter",
|
||||
[~CarouselPresenter.IsVirtualizedProperty] = control[~Carousel.IsVirtualizedProperty], |
//// [~CarouselPresenter.IsVirtualizedProperty] = control[~Carousel.IsVirtualizedProperty],
|
||||
[~CarouselPresenter.ItemsPanelProperty] = control[~Carousel.ItemsPanelProperty], |
//// [~CarouselPresenter.ItemsPanelProperty] = control[~Carousel.ItemsPanelProperty],
|
||||
[~CarouselPresenter.SelectedIndexProperty] = control[~Carousel.SelectedIndexProperty], |
//// [~CarouselPresenter.SelectedIndexProperty] = control[~Carousel.SelectedIndexProperty],
|
||||
[~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty], |
//// [~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty],
|
||||
}.RegisterInNameScope(scope); |
//// }.RegisterInNameScope(scope);
|
||||
} |
//// }
|
||||
|
|
||||
private static TextBlock GetContainerTextBlock(object control) |
//// private static TextBlock GetContainerTextBlock(object control)
|
||||
{ |
//// {
|
||||
var contentPresenter = Assert.IsType<ContentPresenter>(control); |
//// var contentPresenter = Assert.IsType<ContentPresenter>(control);
|
||||
contentPresenter.UpdateChild(); |
//// contentPresenter.UpdateChild();
|
||||
return Assert.IsType<TextBlock>(contentPresenter.Child); |
//// return Assert.IsType<TextBlock>(contentPresenter.Child);
|
||||
} |
//// }
|
||||
|
|
||||
[Fact] |
//// [Fact]
|
||||
public void SelectedItem_Validation() |
//// public void SelectedItem_Validation()
|
||||
{ |
//// {
|
||||
using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) |
//// using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
|
||||
{ |
//// {
|
||||
var target = new Carousel |
//// var target = new Carousel
|
||||
{ |
//// {
|
||||
Template = new FuncControlTemplate<Carousel>(CreateTemplate), |
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
||||
IsVirtualized = false |
//// IsVirtualized = false
|
||||
}; |
//// };
|
||||
|
|
||||
target.ApplyTemplate(); |
//// target.ApplyTemplate();
|
||||
((Control)target.Presenter).ApplyTemplate(); |
//// ((Control)target.Presenter).ApplyTemplate();
|
||||
|
|
||||
var exception = new System.InvalidCastException("failed validation"); |
//// var exception = new System.InvalidCastException("failed validation");
|
||||
var textObservable = |
//// var textObservable =
|
||||
new BehaviorSubject<BindingNotification>(new BindingNotification(exception, |
//// new BehaviorSubject<BindingNotification>(new BindingNotification(exception,
|
||||
BindingErrorType.DataValidationError)); |
//// BindingErrorType.DataValidationError));
|
||||
target.Bind(ComboBox.SelectedItemProperty, textObservable); |
//// target.Bind(ComboBox.SelectedItemProperty, textObservable);
|
||||
|
|
||||
Assert.True(DataValidationErrors.GetHasErrors(target)); |
//// Assert.True(DataValidationErrors.GetHasErrors(target));
|
||||
Assert.True(DataValidationErrors.GetErrors(target).SequenceEqual(new[] { exception })); |
//// 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