From 3c0ef0d3df372f134fe4eccc1a2bea84785195f6 Mon Sep 17 00:00:00 2001 From: Bhavanesh N Date: Mon, 23 Jun 2025 00:18:46 +0530 Subject: [PATCH 1/5] Add a new specificity FromUnknown --- src/Controls/src/Core/BindableObject.cs | 28 ++++++++++++++++------ src/Controls/src/Core/SetterSpecificity.cs | 11 +++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/Controls/src/Core/BindableObject.cs b/src/Controls/src/Core/BindableObject.cs index f205dbe7e611..fd10ed6f5d22 100644 --- a/src/Controls/src/Core/BindableObject.cs +++ b/src/Controls/src/Core/BindableObject.cs @@ -129,9 +129,9 @@ void ClearValueCore(BindableProperty property, SetterSpecificity specificity) return; var original = bpcontext.Values.GetSpecificityAndValue(); - if (original.Key == SetterSpecificity.FromHandler) + if (original.Key == SetterSpecificity.FromHandler || original.Key == SetterSpecificity.FromUnknown) { - bpcontext.Values.Remove(SetterSpecificity.FromHandler); + bpcontext.Values.Remove(original.Key); } var newValue = bpcontext.Values.GetClearedValue(specificity); @@ -286,7 +286,7 @@ internal void RemoveBinding(BindableProperty property, SetterSpecificity specifi /// The bindable property on which to apply . /// The binding to set for . public void SetBinding(BindableProperty targetProperty, BindingBase binding) - => SetBinding(targetProperty, binding, binding != null && targetProperty != null && binding.GetRealizedMode(targetProperty) == BindingMode.TwoWay ? SetterSpecificity.FromHandler : SetterSpecificity.FromBinding); + => SetBinding(targetProperty, binding, binding != null && targetProperty != null && binding.GetRealizedMode(targetProperty) == BindingMode.TwoWay ? SetterSpecificity.FromUnknown : SetterSpecificity.FromBinding); internal void SetBinding(BindableProperty targetProperty, BindingBase binding, SetterSpecificity specificity) { @@ -613,11 +613,11 @@ void SetValueActual(BindableProperty property, BindablePropertyContext context, var original = specificityAndValue.Value; var originalSpecificity = specificityAndValue.Key; - //if the last value was set from handler, override it - if (specificity != SetterSpecificity.FromHandler - && originalSpecificity == SetterSpecificity.FromHandler) + // If the originalSpecificity is FromHandler or FromUnknown, + // determine whether it should be overridden. + if (ShouldOverrideSpecificity(originalSpecificity, specificity)) { - context.Values.Remove(SetterSpecificity.FromHandler); + context.Values.Remove(originalSpecificity); originalSpecificity = context.Values.GetSpecificity(); } @@ -716,6 +716,20 @@ void ApplyBinding(BindablePropertyContext context, bool fromBindingContextChange binding.Apply(BindingContext, this, context.Property, fromBindingContextChanged, specificity); } + static bool ShouldOverrideSpecificity(SetterSpecificity previous, SetterSpecificity current) + { + // FromHandler gets overridden by anything except itself + if (previous == SetterSpecificity.FromHandler) + return current != SetterSpecificity.FromHandler; + + // FromUnknown gets overridden by anything except itself + if (previous == SetterSpecificity.FromUnknown) + return current != SetterSpecificity.FromUnknown; + + // For all other cases, don't override + return false; + } + static void BindingContextPropertyBindingChanging(BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase) { object context = bindable._inheritedContext?.Target; diff --git a/src/Controls/src/Core/SetterSpecificity.cs b/src/Controls/src/Core/SetterSpecificity.cs index 11d5d2f2d13f..7379390184b3 100644 --- a/src/Controls/src/Core/SetterSpecificity.cs +++ b/src/Controls/src/Core/SetterSpecificity.cs @@ -20,6 +20,7 @@ internal readonly struct SetterSpecificity { const byte ExtrasVsm = 0x01; const byte ExtrasHandler = 0xFF; + const byte ExtrasUnknown = 0xFE; public const ushort ManualTriggerBaseline = 2; @@ -38,12 +39,16 @@ internal readonly struct SetterSpecificity // handler always apply, but are removed when anything else comes in. see SetValueActual public static readonly SetterSpecificity FromHandler = new SetterSpecificity(0xFF, 0, 0, 0, 0, 0, 0, 0); + // Similar to FromHandler, with slightly lower priority + public static readonly SetterSpecificity FromUnknown = new SetterSpecificity(ExtrasUnknown, 0, 0, 0, 0, 0, 0, 0); + // We store all information in one single UInt64 value to have the fastest comparison possible readonly ulong _value; public bool IsDefault => _value == 0ul; public bool IsHandler => _value == 0xFFFFFFFFFFFFFFFF; + public bool IsUnknown => _value == 0xFEFEFEFEFEFEFEFE; public bool IsVsm => (_value & 0x0100000000000000) != 0; public bool IsVsmImplicit => (_value & 0x0000000004000000) != 0; public bool IsManual => ((_value >> 28) & 0xFFFF) == 1; @@ -115,6 +120,12 @@ public SetterSpecificity(byte extras, ushort manual, byte isDynamicResource, byt return; } + if (extras == ExtrasUnknown) + { + _value = 0x8000000000000000; + return; + } + // If no style is set, set it to a value which supersedes any other style value if (style == 0) { From 05ef7e5e6822eb508b5fc6836ef6c735d1774cea Mon Sep 17 00:00:00 2001 From: Bhavanesh N Date: Mon, 23 Jun 2025 00:20:16 +0530 Subject: [PATCH 2/5] Use FromUnknown instead of FromHandler in all relevant places --- src/Controls/src/Core/BindableObjectExtensions.cs | 2 +- .../Handlers/ListView/Android/EntryCellRenderer.cs | 2 +- .../Handlers/ListView/iOS/EntryCellRenderer.cs | 2 +- src/Controls/src/Core/ListView/ListView.cs | 4 ++-- src/Controls/src/Core/Picker/Picker.cs | 12 ++++++------ src/Controls/src/Core/RadioButton/RadioButton.cs | 6 +++--- .../Core/RadioButton/RadioButtonGroupController.cs | 2 +- src/Controls/src/Core/Shell/SearchHandler.cs | 4 ++-- src/Controls/src/Core/Shell/ShellItem.cs | 2 +- src/Controls/src/Core/Shell/ShellSection.cs | 2 +- src/Controls/src/Core/Window/Window.cs | 8 ++++---- .../Xaml/Diagnostics/BindablePropertyDiagnostics.cs | 2 +- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Controls/src/Core/BindableObjectExtensions.cs b/src/Controls/src/Core/BindableObjectExtensions.cs index bceb514ebe83..e07f247feff9 100644 --- a/src/Controls/src/Core/BindableObjectExtensions.cs +++ b/src/Controls/src/Core/BindableObjectExtensions.cs @@ -25,7 +25,7 @@ internal static void RefreshPropertyValue(this BindableObject self, BindableProp else { // support normal/code properties - self.SetValue(property, value, SetterSpecificity.FromHandler); + self.SetValue(property, value, SetterSpecificity.FromUnknown); } } diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/EntryCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/EntryCellRenderer.cs index 3a8d5794a201..d615261c0ddf 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/EntryCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/EntryCellRenderer.cs @@ -89,7 +89,7 @@ void OnTextChanged(string text) var entryCell = (EntryCell)Cell; entryCell - .SetValue(EntryCell.TextProperty, text, specificity: SetterSpecificity.FromHandler); + .SetValue(EntryCell.TextProperty, text, specificity: SetterSpecificity.FromUnknown); } void UpdateHeight() diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs index 3b506c726b52..14e26be324ba 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs @@ -96,7 +96,7 @@ static void OnTextFieldTextChanged(object sender, EventArgs eventArgs) var model = (EntryCell)cell.Cell; model - .SetValue(EntryCell.TextProperty, cell.TextField.Text, specificity: SetterSpecificity.FromHandler); + .SetValue(EntryCell.TextProperty, cell.TextField.Text, specificity: SetterSpecificity.FromUnknown); } static void UpdateHorizontalTextAlignment(EntryCellTableViewCell cell, EntryCell entryCell) diff --git a/src/Controls/src/Core/ListView/ListView.cs b/src/Controls/src/Core/ListView/ListView.cs index 73e14502dd54..70734b72b284 100644 --- a/src/Controls/src/Core/ListView/ListView.cs +++ b/src/Controls/src/Core/ListView/ListView.cs @@ -521,7 +521,7 @@ public void NotifyRowTapped(int groupIndex, int inGroupIndex, Cell cell = null) // Set SelectedItem before any events so we don't override any changes they may have made. if (SelectionMode != ListViewSelectionMode.None) - SetValueCore(SelectedItemProperty, cell?.BindingContext, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource | (changed ? SetValueFlags.RaiseOnEqual : 0), SetValuePrivateFlags.Default, SetterSpecificity.FromHandler); + SetValueCore(SelectedItemProperty, cell?.BindingContext, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource | (changed ? SetValueFlags.RaiseOnEqual : 0), SetValuePrivateFlags.Default, SetterSpecificity.FromUnknown); cell?.OnTapped(); @@ -547,7 +547,7 @@ public void NotifyRowTapped(int groupIndex, int inGroupIndex, Cell cell, bool is // Set SelectedItem before any events so we don't override any changes they may have made. if (SelectionMode != ListViewSelectionMode.None) - SetValueCore(SelectedItemProperty, cell?.BindingContext, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource | (changed ? SetValueFlags.RaiseOnEqual : 0), SetValuePrivateFlags.Default, SetterSpecificity.FromHandler); + SetValueCore(SelectedItemProperty, cell?.BindingContext, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource | (changed ? SetValueFlags.RaiseOnEqual : 0), SetValuePrivateFlags.Default, SetterSpecificity.FromUnknown); if (isContextMenuRequested || cell == null) { diff --git a/src/Controls/src/Core/Picker/Picker.cs b/src/Controls/src/Core/Picker/Picker.cs index 399235196aa4..8a44c9bc00f7 100644 --- a/src/Controls/src/Core/Picker/Picker.cs +++ b/src/Controls/src/Core/Picker/Picker.cs @@ -372,7 +372,7 @@ void ClampSelectedIndex() var oldIndex = SelectedIndex; var newIndex = SelectedIndex.Clamp(-1, Items.Count - 1); //FIXME use the specificity of the caller - SetValue(SelectedIndexProperty, newIndex, SetterSpecificity.FromHandler); + SetValue(SelectedIndexProperty, newIndex, SetterSpecificity.FromUnknown); // If the index has not changed, still need to change the selected item if (newIndex == oldIndex) UpdateSelectedItem(newIndex); @@ -384,10 +384,10 @@ void UpdateSelectedIndex(object selectedItem) if (ItemsSource != null) { - SetValue(SelectedIndexProperty, ItemsSource.IndexOf(selectedItem), SetterSpecificity.FromHandler); + SetValue(SelectedIndexProperty, ItemsSource.IndexOf(selectedItem), SetterSpecificity.FromUnknown); return; } - SetValue(SelectedIndexProperty, Items.IndexOf(selectedItem), SetterSpecificity.FromHandler); + SetValue(SelectedIndexProperty, Items.IndexOf(selectedItem), SetterSpecificity.FromUnknown); } void UpdateSelectedItem(int index) @@ -396,18 +396,18 @@ void UpdateSelectedItem(int index) if (index == -1) { - SetValue(SelectedItemProperty, null, SetterSpecificity.FromHandler); + SetValue(SelectedItemProperty, null, SetterSpecificity.FromUnknown); return; } if (ItemsSource != null) { var item = index < ItemsSource.Count ? ItemsSource[index] : null; - SetValue(SelectedItemProperty, item, SetterSpecificity.FromHandler); + SetValue(SelectedItemProperty, item, SetterSpecificity.FromUnknown); return; } - SetValue(SelectedItemProperty, Items[index], SetterSpecificity.FromHandler); + SetValue(SelectedItemProperty, Items[index], SetterSpecificity.FromUnknown); } /// diff --git a/src/Controls/src/Core/RadioButton/RadioButton.cs b/src/Controls/src/Core/RadioButton/RadioButton.cs index 6a40dedf8119..ab25189c940a 100644 --- a/src/Controls/src/Core/RadioButton/RadioButton.cs +++ b/src/Controls/src/Core/RadioButton/RadioButton.cs @@ -365,7 +365,7 @@ void ApplyIsCheckedState() void SelectRadioButton(object sender, EventArgs e) { if (IsEnabled) - SetValue(IsCheckedProperty, true, specificity: SetterSpecificity.FromHandler); + SetValue(IsCheckedProperty, true, specificity: SetterSpecificity.FromUnknown); } void OnIsCheckedPropertyChanged(bool isChecked) @@ -428,7 +428,7 @@ void HandleRadioButtonGroupSelectionChanged(RadioButton selected, RadioButtonGro return; } - SetValue(IsCheckedProperty, false, specificity: SetterSpecificity.FromHandler); + SetValue(IsCheckedProperty, false, specificity: SetterSpecificity.FromUnknown); } void HandleRadioButtonGroupValueChanged(Element layout, RadioButtonGroupValueChanged args) @@ -438,7 +438,7 @@ void HandleRadioButtonGroupValueChanged(Element layout, RadioButtonGroupValueCha return; } - SetValue(IsCheckedProperty, true, specificity: SetterSpecificity.FromHandler); + SetValue(IsCheckedProperty, true, specificity: SetterSpecificity.FromUnknown); } static View BuildDefaultTemplate() diff --git a/src/Controls/src/Core/RadioButton/RadioButtonGroupController.cs b/src/Controls/src/Core/RadioButton/RadioButtonGroupController.cs index 5f19ddccf18f..9556bb3a36c1 100644 --- a/src/Controls/src/Core/RadioButton/RadioButtonGroupController.cs +++ b/src/Controls/src/Core/RadioButton/RadioButtonGroupController.cs @@ -106,7 +106,7 @@ void AddRadioButton(RadioButton radioButton) if (object.Equals(radioButton.Value, this.SelectedValue)) { - radioButton.SetValue(RadioButton.IsCheckedProperty, true, specificity: SetterSpecificity.FromHandler); + radioButton.SetValue(RadioButton.IsCheckedProperty, true, specificity: SetterSpecificity.FromUnknown); } } diff --git a/src/Controls/src/Core/Shell/SearchHandler.cs b/src/Controls/src/Core/Shell/SearchHandler.cs index bf6e893d5197..54c68deb6fb4 100644 --- a/src/Controls/src/Core/Shell/SearchHandler.cs +++ b/src/Controls/src/Core/Shell/SearchHandler.cs @@ -56,7 +56,7 @@ static void OnIsFocusedPropertyChanged(BindableObject bindable, object oldvalue, [EditorBrowsable(EditorBrowsableState.Never)] public void SetIsFocused(bool value) { - SetValue(IsFocusedPropertyKey, value, specificity: SetterSpecificity.FromHandler); + SetValue(IsFocusedPropertyKey, value, specificity: SetterSpecificity.FromUnknown); } [EditorBrowsable(EditorBrowsableState.Never)] public event EventHandler FocusChangeRequested; @@ -297,7 +297,7 @@ void ISearchHandlerController.ClearPlaceholderClicked() void ISearchHandlerController.ItemSelected(object obj) { OnItemSelected(obj); - SetValue(SelectedItemPropertyKey, obj, specificity: SetterSpecificity.FromHandler); + SetValue(SelectedItemPropertyKey, obj, specificity: SetterSpecificity.FromUnknown); } void ISearchHandlerController.QueryConfirmed() diff --git a/src/Controls/src/Core/Shell/ShellItem.cs b/src/Controls/src/Core/Shell/ShellItem.cs index 494bb5c3a7c3..b4c2579a6ca0 100644 --- a/src/Controls/src/Core/Shell/ShellItem.cs +++ b/src/Controls/src/Core/Shell/ShellItem.cs @@ -273,7 +273,7 @@ void OnVisibleChildRemoved(Element child) if (CurrentItem == child) { if (ShellItemController.GetItems().Count == 0) - ClearValue(CurrentItemProperty, specificity: SetterSpecificity.FromHandler); + ClearValue(CurrentItemProperty, specificity: SetterSpecificity.FromUnknown); else SetValueFromRenderer(CurrentItemProperty, ShellItemController.GetItems()[0]); } diff --git a/src/Controls/src/Core/Shell/ShellSection.cs b/src/Controls/src/Core/Shell/ShellSection.cs index 5f648989cde7..5a1e83fa3b39 100644 --- a/src/Controls/src/Core/Shell/ShellSection.cs +++ b/src/Controls/src/Core/Shell/ShellSection.cs @@ -721,7 +721,7 @@ void OnVisibleChildRemoved(Element child) var contentItems = ShellSectionController.GetItems(); if (contentItems.Count == 0) { - ClearValue(CurrentItemProperty, specificity: SetterSpecificity.FromHandler); + ClearValue(CurrentItemProperty, specificity: SetterSpecificity.FromUnknown); } else { diff --git a/src/Controls/src/Core/Window/Window.cs b/src/Controls/src/Core/Window/Window.cs index 270d1f1e4cde..214058467be0 100644 --- a/src/Controls/src/Core/Window/Window.cs +++ b/src/Controls/src/Core/Window/Window.cs @@ -220,10 +220,10 @@ void IWindow.FrameChanged(Rect frame) var shouldTriggerSizeChanged = (Width != frame.Width) || (Height != frame.Height); - SetValue(XProperty, frame.X, SetterSpecificity.FromHandler); - SetValue(YProperty, frame.Y, SetterSpecificity.FromHandler); - SetValue(WidthProperty, frame.Width, SetterSpecificity.FromHandler); - SetValue(HeightProperty, frame.Height, SetterSpecificity.FromHandler); + SetValue(XProperty, frame.X, SetterSpecificity.FromUnknown); + SetValue(YProperty, frame.Y, SetterSpecificity.FromUnknown); + SetValue(WidthProperty, frame.Width, SetterSpecificity.FromUnknown); + SetValue(HeightProperty, frame.Height, SetterSpecificity.FromUnknown); _batchFrameUpdate--; if (_batchFrameUpdate < 0) diff --git a/src/Controls/src/Core/Xaml/Diagnostics/BindablePropertyDiagnostics.cs b/src/Controls/src/Core/Xaml/Diagnostics/BindablePropertyDiagnostics.cs index 2b2452d7de38..12e99dd4cfb6 100644 --- a/src/Controls/src/Core/Xaml/Diagnostics/BindablePropertyDiagnostics.cs +++ b/src/Controls/src/Core/Xaml/Diagnostics/BindablePropertyDiagnostics.cs @@ -28,7 +28,7 @@ public static ValueSource GetValueSource(BindableObject bindable, BindableProper return new ValueSource(BaseValueSource.Style); if (specificity == SetterSpecificity.Trigger) return new ValueSource(BaseValueSource.StyleTrigger); - if (specificity == SetterSpecificity.FromHandler) + if (specificity == SetterSpecificity.FromHandler || specificity == SetterSpecificity.FromUnknown) return new ValueSource(BaseValueSource.Unknown, isCurrent: true); if (specificity.IsVsm) From eacce9247ce5b185119d59fe05e151f3b3f6a935 Mon Sep 17 00:00:00 2001 From: Bhavanesh N Date: Mon, 23 Jun 2025 00:20:42 +0530 Subject: [PATCH 3/5] add Unit tests --- .../Core.UnitTests/BindingBaseUnitTests.cs | 54 ++++++++++++++++++- .../SetterSpecificityListTests.cs | 13 +++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/Controls/tests/Core.UnitTests/BindingBaseUnitTests.cs b/src/Controls/tests/Core.UnitTests/BindingBaseUnitTests.cs index a639edb2293a..18b0c0773c33 100644 --- a/src/Controls/tests/Core.UnitTests/BindingBaseUnitTests.cs +++ b/src/Controls/tests/Core.UnitTests/BindingBaseUnitTests.cs @@ -145,12 +145,64 @@ public void StringFormatTwoWay() var bo = new MockBindable { BindingContext = vm }; bo.SetBinding(property, binding); - bo.SetValue(property, "Baz", SetterSpecificity.FromHandler); + bo.SetValue(property, "Baz", SetterSpecificity.FromUnknown); Assert.Equal("Baz", vm.Text); Assert.Equal("Foo Baz", bo.GetValue(property)); } + [Fact("TwoWay bindings should apply with FromUnknown specificity")] + public void TwoWayBindingSpecificity() + { + var property = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable)); + var binding = new Binding("Text", BindingMode.TwoWay); + + var vm = new MockViewModel { Text = "initial" }; + var bo = new MockBindable { BindingContext = vm }; + bo.SetBinding(property, binding); + + var context = bo.GetContext(property); + Assert.Equal(SetterSpecificity.FromUnknown, context.Values.GetSpecificity()); + } + + [Fact("FromUnknown should override FromHandler")] + public void FromUnknownOverridesFromHandler() + { + var property = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable)); + + var bo = new MockBindable(); + bo.SetValue(property, "HandlerValue", SetterSpecificity.FromHandler); + bo.SetValue(property, "UnknownValue", SetterSpecificity.FromUnknown); + + // Assert value + Assert.Equal("UnknownValue", bo.GetValue(property)); + + // Assert specificity + var context = bo.GetContext(property); + Assert.Equal(SetterSpecificity.FromUnknown, context.Values.GetSpecificity()); + var fromHandlerValue = context.Values[SetterSpecificity.FromHandler]; + Assert.Null(fromHandlerValue); + } + + [Fact("FromHandler should override FromUnknown")] + public void FromHandlerOverridesFromUnknown() + { + var property = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable)); + + var bo = new MockBindable(); + bo.SetValue(property, "UnknownValue", SetterSpecificity.FromUnknown); + bo.SetValue(property, "HandlerValue", SetterSpecificity.FromHandler); + + // Assert value + Assert.Equal("HandlerValue", bo.GetValue(property)); + + // Assert specificity + var context = bo.GetContext(property); + Assert.Equal(SetterSpecificity.FromHandler, context.Values.GetSpecificity()); + var fromUnknownValue = context.Values[SetterSpecificity.FromUnknown]; + Assert.Null(fromUnknownValue); + } + [Fact("You should get an exception when trying to change a binding after it's been applied")] public void ChangeAfterApply() { diff --git a/src/Controls/tests/Core.UnitTests/SetterSpecificityListTests.cs b/src/Controls/tests/Core.UnitTests/SetterSpecificityListTests.cs index d54cde3f52aa..fee1d086a316 100644 --- a/src/Controls/tests/Core.UnitTests/SetterSpecificityListTests.cs +++ b/src/Controls/tests/Core.UnitTests/SetterSpecificityListTests.cs @@ -193,5 +193,18 @@ public void GetClearedValueForSpecificity() Assert.Equal(nameof(SetterSpecificity.DefaultValue), list.GetClearedValue(SetterSpecificity.ManualValueSetter)); Assert.Equal(nameof(SetterSpecificity.ManualValueSetter), list.GetClearedValue(SetterSpecificity.FromHandler)); } + + [Fact] + public void GetClearedValueForFromUnknown() + { + var list = new SetterSpecificityList(); + + list[SetterSpecificity.DefaultValue] = nameof(SetterSpecificity.DefaultValue); + list[SetterSpecificity.ManualValueSetter] = nameof(SetterSpecificity.ManualValueSetter); + list[SetterSpecificity.FromUnknown] = nameof(SetterSpecificity.FromUnknown); + + // Should fall back to ManualValueSetter + Assert.Equal(nameof(SetterSpecificity.ManualValueSetter), list.GetClearedValue(SetterSpecificity.FromUnknown)); + } } } From a1dff3113c3ce0d6d1fced17061d17ed08379b6c Mon Sep 17 00:00:00 2001 From: Bhavanesh N Date: Mon, 23 Jun 2025 00:30:55 +0530 Subject: [PATCH 4/5] change variable name --- src/Controls/src/Core/BindableObject.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Controls/src/Core/BindableObject.cs b/src/Controls/src/Core/BindableObject.cs index fd10ed6f5d22..3818f0d7fec6 100644 --- a/src/Controls/src/Core/BindableObject.cs +++ b/src/Controls/src/Core/BindableObject.cs @@ -716,14 +716,14 @@ void ApplyBinding(BindablePropertyContext context, bool fromBindingContextChange binding.Apply(BindingContext, this, context.Property, fromBindingContextChanged, specificity); } - static bool ShouldOverrideSpecificity(SetterSpecificity previous, SetterSpecificity current) + static bool ShouldOverrideSpecificity(SetterSpecificity orginal, SetterSpecificity current) { // FromHandler gets overridden by anything except itself - if (previous == SetterSpecificity.FromHandler) + if (orginal == SetterSpecificity.FromHandler) return current != SetterSpecificity.FromHandler; // FromUnknown gets overridden by anything except itself - if (previous == SetterSpecificity.FromUnknown) + if (orginal == SetterSpecificity.FromUnknown) return current != SetterSpecificity.FromUnknown; // For all other cases, don't override From 9fef43ad6b07f7e7eb4c8f88b41b0a909a2affe3 Mon Sep 17 00:00:00 2001 From: Bhavanesh N Date: Mon, 23 Jun 2025 00:55:03 +0530 Subject: [PATCH 5/5] Update IsUnknown --- src/Controls/src/Core/SetterSpecificity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controls/src/Core/SetterSpecificity.cs b/src/Controls/src/Core/SetterSpecificity.cs index 7379390184b3..06bfabb4c053 100644 --- a/src/Controls/src/Core/SetterSpecificity.cs +++ b/src/Controls/src/Core/SetterSpecificity.cs @@ -48,7 +48,7 @@ internal readonly struct SetterSpecificity public bool IsDefault => _value == 0ul; public bool IsHandler => _value == 0xFFFFFFFFFFFFFFFF; - public bool IsUnknown => _value == 0xFEFEFEFEFEFEFEFE; + public bool IsUnknown => _value == 0x8000000000000000; public bool IsVsm => (_value & 0x0100000000000000) != 0; public bool IsVsmImplicit => (_value & 0x0000000004000000) != 0; public bool IsManual => ((_value >> 28) & 0xFFFF) == 1;