@ -34,8 +34,13 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="Items"/> property.
/// </summary>
public static readonly DirectProperty < ItemsControl , IEnumerable ? > ItemsProperty =
AvaloniaProperty . RegisterDirect < ItemsControl , IEnumerable ? > ( nameof ( Items ) , o = > o . Items , ( o , v ) = > o . Items = v ) ;
public static readonly DirectProperty < ItemsControl , IList ? > ItemsProperty =
AvaloniaProperty . RegisterDirect < ItemsControl , IList ? > (
nameof ( Items ) ,
o = > o . Items ,
#pragma warning disable CS0618 // Type or member is obsolete
( o , v ) = > o . Items = v ) ;
#pragma warning restore CS0618 // Type or member is obsolete
/// <summary>
/// Defines the <see cref="ItemContainerTheme"/> property.
@ -56,23 +61,23 @@ namespace Avalonia.Controls
AvaloniaProperty . Register < ItemsControl , ITemplate < Panel > > ( nameof ( ItemsPanel ) , DefaultPanel ) ;
/// <summary>
/// Defines the <see cref="ItemTemplat e"/> property.
/// Defines the <see cref="ItemsSourc e"/> property.
/// </summary>
public static readonly StyledProperty < IDataTemplate ? > ItemTemplat eProperty =
AvaloniaProperty . Register < ItemsControl , IDataTemplat e ? > ( nameof ( ItemTemplat e ) ) ;
public static readonly StyledProperty < IEnumerable ? > ItemsSourc eProperty =
AvaloniaProperty . Register < ItemsControl , IEnumerabl e ? > ( nameof ( ItemsSourc e ) ) ;
/// <summary>
/// Defines the <see cref="ItemsView "/> property.
/// Defines the <see cref="ItemTemplate "/> property.
/// </summary>
public static readonly DirectProperty < ItemsControl , ItemsSourceView > ItemsView Property =
AvaloniaProperty . RegisterDirect < ItemsControl , ItemsSourceView > ( nameof ( ItemsView ) , o = > o . ItemsView ) ;
public static readonly StyledProperty < IDataTemplate ? > ItemTemplate Property =
AvaloniaProperty . Register < ItemsControl , IDataTemplate ? > ( nameof ( ItemTemplate ) ) ;
/// <summary>
/// Defines the <see cref="DisplayMemberBinding" /> property
/// </summary>
public static readonly StyledProperty < IBinding ? > DisplayMemberBindingProperty =
AvaloniaProperty . Register < ItemsControl , IBinding ? > ( nameof ( DisplayMemberBinding ) ) ;
/// <summary>
/// Defines the <see cref="AreHorizontalSnapPointsRegular"/> property.
/// </summary>
@ -89,15 +94,14 @@ namespace Avalonia.Controls
/// Gets or sets the <see cref="IBinding"/> to use for binding to the display member of each item.
/// </summary>
[AssignBinding]
[InheritDataTypeFromItems(nameof(Items))]
[InheritDataTypeFromItems(nameof(ItemsSource ))]
public IBinding ? DisplayMemberBinding
{
get = > GetValue ( DisplayMemberBindingProperty ) ;
set = > SetValue ( DisplayMemberBindingProperty , value ) ;
}
private IEnumerable ? _ items = new AvaloniaList < object > ( ) ;
private ItemsSourceView _ itemsView ;
private readonly ItemCollection _ items = new ( ) ;
private int _ itemCount ;
private ItemContainerGenerator ? _ itemContainerGenerator ;
private EventHandler < ChildIndexChangedEventArgs > ? _ childIndexChanged ;
@ -110,9 +114,8 @@ namespace Avalonia.Controls
/// </summary>
public ItemsControl ( )
{
_ itemsView = ItemsSourceView . GetOrCreate ( _ items ) ;
_ itemsView . PostCollectionChanged + = ItemsCollectionChanged ;
UpdatePseudoClasses ( 0 ) ;
UpdatePseudoClasses ( ) ;
_ items . CollectionChanged + = OnItemsViewCollectionChanged ;
}
/// <summary>
@ -129,10 +132,21 @@ namespace Avalonia.Controls
/// Gets or sets the items to display.
/// </summary>
[Content]
public IEnumerable ? Items
public IList ? Items
{
get = > _ items ;
set = > SetAndRaise ( ItemsProperty , ref _ items , value ) ;
get = > _ items . GetItemsPropertyValue ( ) ;
[Obsolete("Use ItemsSource to set or bind items.")]
set
{
var oldItems = _ items . GetItemsPropertyValue ( ) ;
if ( value ! = oldItems )
{
_ items . SetItems ( value ) ;
RaisePropertyChanged ( ItemsProperty , oldItems , value ) ;
}
}
}
/// <summary>
@ -140,17 +154,24 @@ namespace Avalonia.Controls
/// </summary>
public ControlTheme ? ItemContainerTheme
{
get = > GetValue ( ItemContainerThemeProperty ) ;
get = > GetValue ( ItemContainerThemeProperty ) ;
set = > SetValue ( ItemContainerThemeProperty , value ) ;
}
/// <summary>
/// Gets the number of items in <see cref="Items"/>.
/// Gets the number of items be ing displayed by the <see cref="ItemsControl "/>.
/// </summary>
public int ItemCount
{
get = > _ itemCount ;
private set = > SetAndRaise ( ItemCountProperty , ref _ itemCount , value ) ;
private set
{
if ( SetAndRaise ( ItemCountProperty , ref _ itemCount , value ) )
{
UpdatePseudoClasses ( ) ;
_ childIndexChanged ? . Invoke ( this , ChildIndexChangedEventArgs . TotalCountChanged ) ;
}
}
}
/// <summary>
@ -162,13 +183,44 @@ namespace Avalonia.Controls
set = > SetValue ( ItemsPanelProperty , value ) ;
}
/// <summary>
/// Gets or sets a collection used to generate the content of the <see cref="ItemsControl"/>.
/// </summary>
/// <remarks>
/// Since Avalonia 11, <see cref="ItemsControl"/> has both an <see cref="Items"/> property
/// and an <see cref="ItemsSource"/> property. The properties have the following differences:
///
/// <list type="bullet">
/// <item><see cref="Items"/> is initialized with an empty collection and is a direct property,
/// meaning that it cannot be styled </item>
/// <item><see cref="ItemsSource"/> is by default null, and is a styled property. This property
/// is marked as the content property and will be used for items added via inline XAML.</item>
/// </list>
///
/// In Avalonia 11 the two properties can be used almost interchangeably but this will change
/// in a later version. In order to be ready for this change, follow the following guidance:
///
/// <list type="bullet">
/// <item>You should use the <see cref="Items"/> property when you're assigning a collection of
/// item containers directly, for example adding a collection of <see cref="ListBoxItem"/>s
/// directly to a <see cref="ListBox"/>.</item>
/// <item>You should use the <see cref="ItemsSource"/> property when you're assigning or
/// binding a collection of models which will be transformed by a data template.</item>
/// </list>
/// </remarks>
public IEnumerable ? ItemsSource
{
get = > GetValue ( ItemsSourceProperty ) ;
set = > SetValue ( ItemsSourceProperty , value ) ;
}
/// <summary>
/// Gets or sets the data template used to display the items in the control.
/// </summary>
[InheritDataTypeFromItems(nameof(Items))]
[InheritDataTypeFromItems(nameof(ItemsSource ))]
public IDataTemplate ? ItemTemplate
{
get = > GetValue ( ItemTemplateProperty ) ;
get = > GetValue ( ItemTemplateProperty ) ;
set = > SetValue ( ItemTemplateProperty , value ) ;
}
@ -182,32 +234,7 @@ namespace Avalonia.Controls
/// </summary>
public Panel ? ItemsPanelRoot = > Presenter ? . Panel ;
/// <summary>
/// Gets a standardized view over <see cref="Items"/>.
/// </summary>
/// <remarks>
/// The <see cref="Items"/> property may be an enumerable which does not implement
/// <see cref="IList"/> or may be null. This view can be used to provide a standardized
/// view of the current items regardless of the type of the concrete collection, and
/// without having to deal with null values.
/// </remarks>
public ItemsSourceView ItemsView
{
get = > _ itemsView ;
private set
{
if ( ReferenceEquals ( _ itemsView , value ) )
return ;
var oldValue = _ itemsView ;
RemoveControlItemsFromLogicalChildren ( _ itemsView ) ;
_ itemsView . PostCollectionChanged - = ItemsCollectionChanged ;
_ itemsView = value ;
_ itemsView . PostCollectionChanged + = ItemsCollectionChanged ;
AddControlItemsToLogicalChildren ( _ itemsView ) ;
RaisePropertyChanged ( ItemsViewProperty , oldValue , _ itemsView ) ;
}
}
public ItemCollection ItemsView = > _ items ;
private protected bool WrapFocus { get ; set ; }
@ -262,7 +289,7 @@ namespace Avalonia.Controls
/// </summary>
public bool AreHorizontalSnapPointsRegular
{
get = > GetValue ( AreHorizontalSnapPointsRegularProperty ) ;
get = > GetValue ( AreHorizontalSnapPointsRegularProperty ) ;
set = > SetValue ( AreHorizontalSnapPointsRegularProperty , value ) ;
}
@ -271,7 +298,7 @@ namespace Avalonia.Controls
/// </summary>
public bool AreVerticalSnapPointsRegular
{
get = > GetValue ( AreVerticalSnapPointsRegularProperty ) ;
get = > GetValue ( AreVerticalSnapPointsRegularProperty ) ;
set = > SetValue ( AreVerticalSnapPointsRegularProperty , value ) ;
}
@ -295,7 +322,7 @@ namespace Avalonia.Controls
/// </returns>
public Control ? ContainerFromItem ( object item )
{
var index = ItemsView . IndexOf ( item ) ;
var index = _ items . IndexOf ( item ) ;
return index > = 0 ? ContainerFromIndex ( index ) : null ;
}
@ -319,7 +346,7 @@ namespace Avalonia.Controls
public object? ItemFromContainer ( Control container )
{
var index = IndexFromContainer ( container ) ;
return index > = 0 & & index < ItemsView . Count ? ItemsView [ index ] : null ;
return index > = 0 & & index < _ items . Count ? _ items [ index ] : null ;
}
/// <summary>
@ -389,7 +416,7 @@ namespace Avalonia.Controls
if ( itemTemplate is ITreeDataTemplate treeTemplate )
{
if ( item is not null & & treeTemplate . ItemsSelector ( item ) is { } itemsBinding )
BindingOperations . Apply ( hic , ItemsProperty , itemsBinding , null ) ;
BindingOperations . Apply ( hic , ItemsSource Property , itemsBinding , null ) ;
}
}
}
@ -485,19 +512,13 @@ namespace Avalonia.Controls
{
base . OnPropertyChanged ( change ) ;
if ( change . Property = = ItemsProperty )
{
ItemsView = ItemsSourceView . GetOrCreate ( change . GetNewValue < IEnumerable ? > ( ) ) ;
ItemCount = ItemsView . Count ;
}
else if ( change . Property = = ItemCountProperty )
if ( change . Property = = ItemContainerThemeProperty & & _ itemContainerGenerator is not null )
{
UpdatePseudoClasses ( change . GetNewValue < int > ( ) ) ;
_ childIndexChanged ? . Invoke ( this , ChildIndexChangedEventArgs . TotalCountChanged ) ;
RefreshContainers ( ) ;
}
else if ( change . Property = = ItemContainerThemeProperty & & _ itemContainerGenerator is not null )
else if ( change . Property = = ItemsSourceProperty )
{
RefreshContainers ( ) ;
_ items . SetItemsSource ( change . GetNewValue < IEnumerable ? > ( ) ) ;
}
else if ( change . Property = = ItemTemplateProperty )
{
@ -524,14 +545,12 @@ namespace Avalonia.Controls
/// <summary>
/// Called when the <see cref="INotifyCollectionChanged.CollectionChanged"/> event is
/// raised on <see cref="Items"/>.
/// raised on <see cref="ItemsView "/>.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
protected virtual void ItemsCollectionChanged ( object? sender , NotifyCollectionChangedEventArgs e )
private pr otected virtual void On ItemsView CollectionChanged( object? sender , NotifyCollectionChangedEventArgs e )
{
ItemCount = _ itemsView . Count ;
switch ( e . Action )
{
case NotifyCollectionChangedAction . Add :
@ -542,6 +561,8 @@ namespace Avalonia.Controls
RemoveControlItemsFromLogicalChildren ( e . OldItems ) ;
break ;
}
ItemCount = ItemsView . Count ;
}
/// <summary>
@ -585,7 +606,7 @@ namespace Avalonia.Controls
{
var itemContainerTheme = ItemContainerTheme ;
if ( itemContainerTheme is not null & &
if ( itemContainerTheme is not null & &
! container . IsSet ( ThemeProperty ) & &
( ( IStyleable ) container ) . StyleKey = = itemContainerTheme . TargetType )
{
@ -616,10 +637,6 @@ namespace Avalonia.Controls
ClearContainerForItemOverride ( container ) ;
}
/// <summary>
/// Given a collection of items, adds those that are controls to the logical children.
/// </summary>
/// <param name="items">The items.</param>
private void AddControlItemsToLogicalChildren ( IEnumerable ? items )
{
if ( items is null )
@ -640,10 +657,6 @@ namespace Avalonia.Controls
LogicalChildren . AddRange ( toAdd ) ;
}
/// <summary>
/// Given a collection of items, removes those that are controls to from logical children.
/// </summary>
/// <param name="items">The items.</param>
private void RemoveControlItemsFromLogicalChildren ( IEnumerable ? items )
{
if ( items is null )
@ -681,10 +694,10 @@ namespace Avalonia.Controls
return _d isplayMemberItemTemplate ;
}
private void UpdatePseudoClasses ( int itemCount )
private void UpdatePseudoClasses ( )
{
PseudoClasses . Set ( ":empty" , i temCount = = 0 ) ;
PseudoClasses . Set ( ":singleitem" , i temCount = = 1 ) ;
PseudoClasses . Set ( ":empty" , I temCount = = 0 ) ;
PseudoClasses . Set ( ":singleitem" , I temCount = = 1 ) ;
}
protected static IInputElement ? GetNextControl (