Add InvalidateStyle() and VisualStateManager.InvalidateVisualStates() APIs#34723
Add InvalidateStyle() and VisualStateManager.InvalidateVisualStates() APIs#34723jfversluis merged 3 commits intonet11.0from
Conversation
Add public APIs to force reapplication of in-place-mutated styles and visual states, primarily for Hot Reload scenarios. - StyleableElement.InvalidateStyle() - forces unapply+reapply of merged style - Span.InvalidateStyle() - same for Span (owns its own MergedStyle) - ImageSource.InvalidateStyle() - same for ImageSource - VisualStateManager.InvalidateVisualStates(VisualElement) - forces unapply+reapply of current visual state setters for all groups Fixes #34721 Fixes #34722 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34723Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34723" |
There was a problem hiding this comment.
Pull request overview
This PR adds public “invalidate” APIs to force reapplication of styles and visual states after in-place mutation (primarily to support Hot Reload / XAML Incremental Hot Reload scenarios where reference-equality short-circuits prevent reapplying).
Changes:
- Added
StyleableElement.InvalidateStyle()(and equivalent APIs onSpanandImageSource) to unapply+reapply the current merged style layers. - Added
VisualStateManager.InvalidateVisualStates(VisualElement)to unapply+reapply setters for each group’s current state. - Added unit tests covering style and VSM invalidation/no-op cases, and updated PublicAPI.Unshipped entries across TFMs.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/Controls/src/Core/StyleableElement/StyleableElement.cs | Adds public InvalidateStyle() forwarding to merged style reapply. |
| src/Controls/src/Core/MergedStyle.cs | Adds internal Reapply() helper which unapplies/applies style layers. |
| src/Controls/src/Core/Span.cs | Adds public InvalidateStyle() for Span-owned merged style. |
| src/Controls/src/Core/ImageSource.cs | Adds public InvalidateStyle() for ImageSource-owned merged style. |
| src/Controls/src/Core/VisualStateManager.cs | Adds InvalidateVisualStates(VisualElement) to reapply current visual state setters. |
| src/Controls/tests/Core.UnitTests/StyleTests.cs | Adds unit tests validating style invalidation behavior and no-op safety. |
| src/Controls/tests/Core.UnitTests/VisualStateManagerTests.cs | Adds unit tests validating VSM invalidation behavior and no-op safety. |
| src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt | Declares new public APIs (netstandard). |
| src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt | Declares new public APIs (net). |
| src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt | Declares new public APIs (Android). |
| src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt | Declares new public APIs (iOS). |
| src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt | Declares new public APIs (MacCatalyst). |
| src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt | Declares new public APIs (Windows). |
| src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt | Declares new public APIs (Tizen). |
| return; | ||
| } | ||
|
|
||
| var vsgSpecificity = vsgSpecificityValue.Key; |
There was a problem hiding this comment.
InvalidateVisualStates computes vsgSpecificity from the property context but never assigns it back to groups.Specificity (unlike GoToState). VisualStateGroupsPropertyChanged relies on VisualStateGroupList.Specificity when unapplying old state setters; leaving it stale can prevent proper unapply if the groups are later replaced/cleared after an invalidation. Consider setting groups.Specificity = vsgSpecificity before computing specificity to keep the list consistent with how its setters were applied.
| var vsgSpecificity = vsgSpecificityValue.Key; | |
| var vsgSpecificity = vsgSpecificityValue.Key; | |
| groups.Specificity = vsgSpecificity; |
| } | ||
|
|
||
| var vsgSpecificity = vsgSpecificityValue.Key; | ||
| var specificity = vsgSpecificity.CopyStyle(1, 0, 0, 0); |
There was a problem hiding this comment.
The arguments are not at all obvious to me. What do the 1 and 0 mean here? why aren't we un-applying and re-applying everything with the original specificity?
| /// Forces unapply and reapply of the current merged style. | ||
| /// Use when a <see cref="Style"/> has been mutated in-place and needs to be reflected on the element. | ||
| /// </summary> | ||
| public void InvalidateStyle() => _mergedStyle.Reapply(); |
There was a problem hiding this comment.
If this is an API intended just for internal use by hot reload, should we make it at least [EditorBrowseable(Never)]? Should the comment discourage people from using it in their codebases?
These APIs are intended for infrastructure use (Hot Reload) and should not appear in IntelliSense for application developers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eVisualStates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace non-existent PR numbers (#34000, #33500, #33000, #34100) with real merged PRs (#34024, #34727, #31202, #28713, #34723) - Add "in dotnet/maui" to all prompts to prevent agent asking for repo - All PRs verified as real merged PRs with actual code changes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add moniker-gated sections documenting: - StyleableElement.InvalidateStyle() in the XAML styles article - VisualStateManager.InvalidateVisualStates() in the visual states article These caller-driven APIs were added in dotnet/maui#34723 for .NET 11 Preview 3 to support in-place style/visual-state mutation scenarios. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Demonstrates two new .NET MAUI 11 Preview 3 APIs (dotnet/maui#34723): - StyleableElement.InvalidateStyle() for reapplying mutated styles - VisualStateManager.InvalidateVisualStates() for reapplying visual states Includes docregion markers for snippet extraction. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Adds public APIs to force reapplication of in-place-mutated styles and visual states, primarily for Hot Reload scenarios (including XAML Incremental Hot Reload).
Problem
When a
StyleorVisualStateobject is mutated in-place (e.g., a Setter'sValueis changed), the framework does not reapply it:MergedStyleuses reference equality (_style == value) to skip reapplicationVisualStateManager.GoToStateshort-circuits whenCurrentState.Name == nameThe only workaround was to create new object instances and reassign them.
Solution
StyleableElement.InvalidateStyle()— unconditionally unapplies and reapplies the current merged style (implicit + class + explicit layers):Also added on
SpanandImageSourcewhich independently own their ownMergedStyle.VisualStateManager.InvalidateVisualStates(VisualElement)— unapplies and reapplies current state setters for all groups:Both APIs are caller-driven (no automatic change tracking) and have no effect on elements without styles/VSM groups.
New Public API
Testing
6 new unit tests covering:
InvalidateStyle()InvalidateVisualStates()Fixes #34721
Fixes #34722
Fixes #618