From 313bceea90b59e9c1ef163aa3378113eb542313a Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 4 Feb 2026 03:08:24 +0000 Subject: [PATCH] Remove AutoCompleteBox.BindingEvaluator (#20596) * Remove AutoCompleteBox.BindingEvaluator * Fix AutoCompleteBoxPage sample * Update API suppressions --- api/Avalonia.nupkg.xml | 12 ++ .../Pages/AutoCompleteBoxPage.xaml.cs | 20 +-- .../AutoCompleteBox.Properties.cs | 26 ++- .../AutoCompleteBox/AutoCompleteBox.cs | 153 +++--------------- 4 files changed, 45 insertions(+), 166 deletions(-) diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index 272f5d71c7..1f49b5fa24 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -31,6 +31,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1 + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + CP0001 T:Avalonia.Controls.NativeMenuItemToggleType @@ -79,6 +85,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1 + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + CP0001 T:Avalonia.Controls.NativeMenuItemToggleType diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs index d9e64ecee0..47b6eb7f4a 100644 --- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs @@ -7,30 +7,12 @@ using Avalonia.Controls; using Avalonia.Data; using Avalonia.Data.Converters; using Avalonia.LogicalTree; +using ControlCatalog.Models; namespace ControlCatalog.Pages { public partial class AutoCompleteBoxPage : UserControl { - public class StateData - { - public string Name { get; private set; } - public string Abbreviation { get; private set; } - public string Capital { get; private set; } - - public StateData(string name, string abbreviatoin, string capital) - { - Name = name; - Abbreviation = abbreviatoin; - Capital = capital; - } - - public override string ToString() - { - return Name; - } - } - private static StateData[] BuildAllStates() { return new StateData[] diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs index c6eab7901e..45566f186b 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs @@ -191,6 +191,12 @@ namespace Avalonia.Controls public static readonly StyledProperty InnerRightContentProperty = TextBox.InnerRightContentProperty.AddOwner(); + /// + /// Defines the property. + /// + public static readonly StyledProperty ValueMemberBindingProperty = + AvaloniaProperty.Register(nameof(ValueMemberBinding)); + /// /// Gets or sets the caret index /// @@ -311,26 +317,16 @@ namespace Avalonia.Controls } /// - /// Gets or sets the that - /// is used to get the values for display in the text portion of - /// the - /// control. + /// Gets or sets the that is used to get the values for display in the text portion + /// of the control. /// - /// The object used - /// when binding to a collection property. + /// The object used when binding to a collection property. [AssignBinding] [InheritDataTypeFromItems(nameof(ItemsSource))] public BindingBase? ValueMemberBinding { - get => _valueBindingEvaluator?.ValueBinding; - set - { - if (ValueMemberBinding != value) - { - _valueBindingEvaluator = new BindingEvaluator(value); - OnValueMemberBindingChanged(value); - } - } + get => GetValue(ValueMemberBindingProperty); + set => SetValue(ValueMemberBindingProperty, value); } /// diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs index ffa95285e5..880fd0ac8a 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs @@ -189,7 +189,7 @@ namespace Avalonia.Controls /// /// A control that can provide updated string values from a binding. /// - private BindingEvaluator? _valueBindingEvaluator; + private BindingEvaluator? _valueMemberBindingEvaluator; /// /// A weak subscription for the collection changed event. @@ -439,6 +439,7 @@ namespace Avalonia.Controls if (!_settingItemTemplateFromValueMemberBinding) _itemTemplateIsFromValueMemberBinding = false; } + private void OnValueMemberBindingChanged(BindingBase? value) { if (_itemTemplateIsFromValueMemberBinding) @@ -460,6 +461,16 @@ namespace Avalonia.Controls } } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ValueMemberBindingProperty) + { + OnValueMemberBindingChanged(change.GetNewValue()); + } + } + static AutoCompleteBox() { FocusableProperty.OverrideDefaultValue(true); @@ -1181,25 +1192,6 @@ namespace Avalonia.Controls } } - /// - /// Formats an Item for text comparisons based on Converter - /// and ConverterCulture properties. - /// - /// The object to format. - /// A value indicating whether to clear - /// the data context after the lookup is performed. - /// Formatted Value. - private string? FormatValue(object? value, bool clearDataContext) - { - string? result = FormatValue(value); - if (clearDataContext && _valueBindingEvaluator != null) - { - _valueBindingEvaluator.ClearDataContext(); - } - - return result; - } - /// /// Converts the specified object to a string by using the /// and @@ -1215,9 +1207,13 @@ namespace Avalonia.Controls /// protected virtual string? FormatValue(object? value) { - if (_valueBindingEvaluator != null) + if (ValueMemberBinding is { } valueMemberBinding) { - return _valueBindingEvaluator.GetDynamicValue(value) ?? String.Empty; + _valueMemberBindingEvaluator ??= new(); + _valueMemberBindingEvaluator.UpdateBinding(valueMemberBinding); + var result = _valueMemberBindingEvaluator.Evaluate(value) ?? string.Empty; + _valueMemberBindingEvaluator.ClearDataContext(); + return result; } return value == null ? String.Empty : value.ToString(); @@ -1451,9 +1447,6 @@ namespace Avalonia.Controls _view?.Clear(); _view?.AddRange(_newViewItems); - - // Clear the evaluator to discard a reference to the last item - _valueBindingEvaluator?.ClearDataContext(); } finally { @@ -1638,7 +1631,7 @@ namespace Avalonia.Controls if (top != null) { newSelectedItem = top; - string? topString = FormatValue(top, true); + string? topString = FormatValue(top); // Only replace partially when the two words being the same int minLength = Math.Min(topString?.Length ?? 0, Text?.Length ?? 0); @@ -1744,7 +1737,7 @@ namespace Avalonia.Controls } else if (TextSelector != null) { - text = TextSelector(SearchText, FormatValue(newItem, true)); + text = TextSelector(SearchText, FormatValue(newItem)); } else if (ItemSelector != null) { @@ -1752,7 +1745,7 @@ namespace Avalonia.Controls } else { - text = FormatValue(newItem, true); + text = FormatValue(newItem); } // Update the Text property and the TextBox values @@ -2020,109 +2013,5 @@ namespace Avalonia.Controls return string.Equals(value, text, StringComparison.Ordinal); } } - - // TODO12: Remove, this shouldn't be part of the public API. Use our internal BindingEvaluator instead. - /// - /// A framework element that permits a binding to be evaluated in a new data - /// context leaf node. - /// - /// The type of dynamic binding to return. - public class BindingEvaluator : Control - { - /// - /// Gets or sets the string value binding used by the control. - /// - private BindingBase? _binding; - - /// - /// Identifies the Value dependency property. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002:AvaloniaProperty objects should not be owned by a generic type", - Justification = "This property is not supposed to be used from XAML.")] - public static readonly StyledProperty ValueProperty = - AvaloniaProperty.Register, T>(nameof(Value)); - - /// - /// Gets or sets the data item value. - /// - public T Value - { - get => GetValue(ValueProperty); - set => SetValue(ValueProperty, value); - } - - /// - /// Gets or sets the value binding. - /// - public BindingBase? ValueBinding - { - get => _binding; - set - { - _binding = value; - if (value is not null) - Bind(ValueProperty, value); - } - } - - /// - /// Initializes a new instance of the BindingEvaluator class. - /// - public BindingEvaluator() - { } - - /// - /// Initializes a new instance of the BindingEvaluator class, - /// setting the initial binding to the provided parameter. - /// - /// The initial string value binding. - public BindingEvaluator(BindingBase? binding) - : this() - { - ValueBinding = binding; - } - - /// - /// Clears the data context so that the control does not keep a - /// reference to the last-looked up item. - /// - public void ClearDataContext() - { - DataContext = null; - } - - /// - /// Updates the data context of the framework element and returns the - /// updated binding value. - /// - /// The object to use as the data context. - /// If set to true, this parameter will - /// clear the data context immediately after retrieving the value. - /// Returns the evaluated T value of the bound dependency - /// property. - public T GetDynamicValue(object o, bool clearDataContext) - { - DataContext = o; - T value = Value; - if (clearDataContext) - { - DataContext = null; - } - return value; - } - - /// - /// Updates the data context of the framework element and returns the - /// updated binding value. - /// - /// The object to use as the data context. - /// Returns the evaluated T value of the bound dependency - /// property. - public T GetDynamicValue(object? o) - { - DataContext = o; - return Value; - } - } } }