Proposal: Add deferral to VisualState Setters for x:Load="False" #801
Labels
area-Styling
feature proposal
New feature proposal
needs-winui-3
Indicates that feature can only be done in WinUI 3.0 or beyond. (needs winui 3)
team-Controls
Issue for the Controls team
Proposal: Add deferral to VisualState Setters for x:Load="False"
Summary
One of the mechanisms for lazy-loading x:Load="False" elements is via targeting the element in a VisualState Setter. This allows for rarely-used visual elements, for example a progress bar rectangle within a button, to be loaded only when needed. This could be done by defining, say, a "ProgressStates" visual state group, and loading the conditional element only in the "Progressing" state.
The issue is that these conditional elements can't interact meaningfully with other visual state groups without being loaded as a side effect. Continuing the above example, if the button had a "FocusStates" visual state group and wanted to style the rectangle's background when focused, it would need to have a Setter in the "Focused" state to set the background color. However, this would mean that entering the "Focused" state would always load the background rectangle, defeating the purpose of the conditionalization.
The proposal would be to allow a VisualState Setter to do a "best effort" assignment to a x:Load=false element. If the element is already realized, the assignment would "take," and if not, it'd take only once the element were realized. This would be an opt-in for VisualState Setter syntax, with the default being the current behavior of realizing the x:Load=false element.
Doing this would allow one VisualStateGroup to define the load behavior of these conditional elements, and others to act upon those elements opportunistically. The end result would be flexible control templates that become pay-for-play but can still be richly styled in markup.
Rationale
To emulate the above behavior, currently only the following options exist:
None of these are particularly attractive alternatives. Option 1 potentially duplicates a lot of styling in the process (and combines into a matrix of styles depending on how many optional elements exist). Option 2 requires a lot of code-behind, reduces designer flexibility, and doesn't scale well either. Option 3 essentially gives up on any load-on-demand optimization offered by XAML.
This feature would make it much easier for developers to concisely author progressively-complex styling, allowing for more efficient use of the XAML runtime.
Scope
Important Notes
As a proposal, I've added a Boolean property, Setter.Defer. If set to True, it will not trigger x:Load realization for its target element, applying when the element is loaded only. Example usage is as follows. When the button using this template goes to the "Focused" state having never entered the "Progressing" state, the setter for "ConditionallyPresentRect.Background" would do nothing, as its associated element had not yet been realized. The formulation "Defer='True'" is what would suppress this realization. After entering the "Progressing" state, the Setter for "ConditionallyPresentRect.Visibility" would realize the element as this is a normal, non-deferring Setter. Thereafter, the setter for "Focused" state would work as expected.
Note also that a deferred Setter would also defer setting properties on descendants of a x:Load=false element silently, and would apply when the ancestor is realized.
Open Questions
The text was updated successfully, but these errors were encountered: