From 18daa4cf37c77da4642de2074fb90ad6d914aa9f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 28 Mar 2023 18:34:26 +0200 Subject: [PATCH 1/3] Make ItemsControl.Items read-only. --- src/Avalonia.Controls/ItemCollection.cs | 14 ---- src/Avalonia.Controls/ItemsControl.cs | 91 ++++++------------------- 2 files changed, 20 insertions(+), 85 deletions(-) diff --git a/src/Avalonia.Controls/ItemCollection.cs b/src/Avalonia.Controls/ItemCollection.cs index c9265558f0..03f46551c5 100644 --- a/src/Avalonia.Controls/ItemCollection.cs +++ b/src/Avalonia.Controls/ItemCollection.cs @@ -106,19 +106,6 @@ namespace Avalonia.Controls } } - internal IList? GetItemsPropertyValue() - { - if (_mode == Mode.ObsoleteItemsSetter) - return Source == s_uninitialized ? null : Source; - return this; - } - - internal void SetItems(IList? items) - { - _mode = Mode.ObsoleteItemsSetter; - SetSource(items ?? s_uninitialized); - } - internal void SetItemsSource(IEnumerable? value) { if (_mode != Mode.ItemsSource && Count > 0) @@ -159,7 +146,6 @@ namespace Avalonia.Controls { Items, ItemsSource, - ObsoleteItemsSetter, } } } diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 1123f42afa..6a91428147 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -30,17 +30,6 @@ namespace Avalonia.Controls private static readonly FuncTemplate DefaultPanel = new(() => new StackPanel()); - /// - /// Defines the property. - /// - public static readonly DirectProperty ItemsProperty = - AvaloniaProperty.RegisterDirect( - 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 - /// /// Defines the property. /// @@ -94,7 +83,6 @@ namespace Avalonia.Controls /// [AssignBinding] [InheritDataTypeFromItems(nameof(ItemsSource))] - [InheritDataTypeFromItems(nameof(Items))] public IBinding? DisplayMemberBinding { get => GetValue(DisplayMemberBindingProperty); @@ -129,48 +117,20 @@ namespace Avalonia.Controls } /// - /// Gets or sets the items to display. + /// Gets the items to display. /// /// - /// Since Avalonia 11, has both an property - /// and an property. The properties have the following differences: - /// - /// - /// is initialized with an empty collection and is a direct property, - /// meaning that it cannot be styled - /// 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. - /// - /// - /// 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: - /// - /// - /// You should use the property when you're assigning a collection of - /// item containers directly, for example adding a collection of s - /// directly to a . Add the containers to the pre-existing list, do not - /// reassign the property via the setter or with a binding. - /// You should use the property when you're assigning or - /// binding a collection of models which will be transformed by a data template. - /// + /// You use either the or the property to + /// specify the collection that should be used to generate the content of your + /// . When the property is set, the + /// collection is made read-only and fixed-size. + /// + /// When is in use, setting the + /// property to null removes the collection and restores usage to , + /// which will be an empty . /// [Content] - public IList? Items - { - 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); - } - } - } + public ItemCollection Items => _items; /// /// Gets or sets the that is applied to the container element generated for each item. @@ -210,27 +170,17 @@ namespace Avalonia.Controls /// Gets or sets a collection used to generate the content of the . /// /// - /// Since Avalonia 11, has both an property - /// and an property. The properties have the following differences: - /// - /// - /// is initialized with an empty collection and is a direct property, - /// meaning that it cannot be styled - /// 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. - /// - /// - /// 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: + /// A common scenario is to use an such as a + /// to display a data collection, or to bind an + /// to a collection object. To bind an + /// to a collection object, use the property. /// - /// - /// You should use the property when you're assigning a collection of - /// item containers directly, for example adding a collection of s - /// directly to a . Add the containers to the pre-existing list, do not - /// reassign the property via the setter or with a binding. - /// You should use the property when you're assigning or - /// binding a collection of models which will be transformed by a data template. - /// + /// When the property is set, the collection + /// is made read-only and fixed-size. + /// + /// When is in use, setting the property to null removes the + /// collection and restores usage to , which will be an empty + /// . /// public IEnumerable? ItemsSource { @@ -242,7 +192,6 @@ namespace Avalonia.Controls /// Gets or sets the data template used to display the items in the control. /// [InheritDataTypeFromItems(nameof(ItemsSource))] - [InheritDataTypeFromItems(nameof(Items))] public IDataTemplate? ItemTemplate { get => GetValue(ItemTemplateProperty); From 996578f2bf67b0001d63cce08afb6a84d1e376bb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 28 Mar 2023 18:34:43 +0200 Subject: [PATCH 2/3] Remove all remaining uses of Items setter. --- samples/BindingDemo/MainWindow.xaml | 4 ++-- samples/ControlCatalog/Pages/TabControlPage.xaml | 2 +- samples/VirtualizationDemo/MainWindow.xaml | 8 ++++---- src/Avalonia.Controls/Flyouts/MenuFlyout.cs | 2 +- src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs | 2 +- .../Xaml/ControlBindingTests.cs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/samples/BindingDemo/MainWindow.xaml b/samples/BindingDemo/MainWindow.xaml index 08ac0426ea..d1c65ca73b 100644 --- a/samples/BindingDemo/MainWindow.xaml +++ b/samples/BindingDemo/MainWindow.xaml @@ -75,11 +75,11 @@ - + - + diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml b/samples/ControlCatalog/Pages/TabControlPage.xaml index a775056ebe..3a2464e9fd 100644 --- a/samples/ControlCatalog/Pages/TabControlPage.xaml +++ b/samples/ControlCatalog/Pages/TabControlPage.xaml @@ -51,7 +51,7 @@ Text="From DataTemplate"> diff --git a/samples/VirtualizationDemo/MainWindow.xaml b/samples/VirtualizationDemo/MainWindow.xaml index 235f3ef2cc..3aee63c246 100644 --- a/samples/VirtualizationDemo/MainWindow.xaml +++ b/samples/VirtualizationDemo/MainWindow.xaml @@ -11,7 +11,7 @@ Margin="16 0 0 0" Width="150" Spacing="4"> - Horiz. ScrollBar - Vert. ScrollBar - - + - + From 060e290fc1a77451b739135f17aa25176956738b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 28 Mar 2023 18:36:33 +0200 Subject: [PATCH 3/3] Revert "Support multiple `InheritDataTypeFromItems`." This reverts commit 57c997bed79a73ffa4bdb6d10a04c24077aff744. --- .../InheritDataTypeFromItemsAttribute.cs | 2 +- .../Primitives/SelectingItemsControl.cs | 1 - ...valoniaXamlIlDataContextTypeTransformer.cs | 43 ++++++++----------- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/Avalonia.Base/Metadata/InheritDataTypeFromItemsAttribute.cs b/src/Avalonia.Base/Metadata/InheritDataTypeFromItemsAttribute.cs index e9bd6ab89f..fac8cd8737 100644 --- a/src/Avalonia.Base/Metadata/InheritDataTypeFromItemsAttribute.cs +++ b/src/Avalonia.Base/Metadata/InheritDataTypeFromItemsAttribute.cs @@ -9,7 +9,7 @@ namespace Avalonia.Metadata; /// A typical usage example is a ListBox control, where is defined on the ItemTemplate property, /// allowing the template to inherit the data type from the Items collection binding. /// -[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)] +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public sealed class InheritDataTypeFromItemsAttribute : Attribute { /// diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index b89a75787f..9c060f2258 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -235,7 +235,6 @@ namespace Avalonia.Controls.Primitives /// [AssignBinding] [InheritDataTypeFromItems(nameof(ItemsSource))] - [InheritDataTypeFromItems(nameof(Items))] public IBinding? SelectedValueBinding { get => GetValue(SelectedValueBindingProperty); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs index 681d2a38d4..a24d4eb6e9 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs @@ -73,32 +73,27 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers // Infer data type from collection binding on a control that displays items. var property = context.ParentNodes().OfType().FirstOrDefault(); var attributeType = context.GetAvaloniaTypes().InheritDataTypeFromItemsAttribute; - var attributes = property?.Property?.GetClrProperty().CustomAttributes - .Where(a => a.Type == attributeType).ToList(); - - if (attributes?.Count > 0) + var attribute = property?.Property?.GetClrProperty().CustomAttributes + .FirstOrDefault(a => a.Type == attributeType); + + if (attribute is not null) { - foreach (var attribute in attributes) + var propertyName = (string)attribute.Parameters.First(); + XamlAstConstructableObjectNode parentObject; + if (attribute.Properties.TryGetValue("AncestorType", out var type) + && type is IXamlType xamlType) { - var propertyName = (string)attribute.Parameters.First(); - XamlAstConstructableObjectNode parentObject; - if (attribute.Properties.TryGetValue("AncestorType", out var type) - && type is IXamlType xamlType) - { - parentObject = context.ParentNodes().OfType() - .FirstOrDefault(n => n.Type.GetClrType().FullName == xamlType.FullName); - } - else - { - parentObject = context.ParentNodes().OfType().FirstOrDefault(); - } - - if (parentObject != null) - { - inferredDataContextTypeNode = InferDataContextOfPresentedItem(context, on, parentObject, propertyName); - if (inferredDataContextTypeNode != null) - break; - } + parentObject = context.ParentNodes().OfType() + .FirstOrDefault(n => n.Type.GetClrType().FullName == xamlType.FullName); + } + else + { + parentObject = context.ParentNodes().OfType().FirstOrDefault(); + } + + if (parentObject != null) + { + inferredDataContextTypeNode = InferDataContextOfPresentedItem(context, on, parentObject, propertyName); } }