[WIP] Analyze effort for adding segmented control for Windows platform#23
Conversation
Co-authored-by: yurkinh <17849938+yurkinh@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR is a WIP step toward adding Windows support to Plugin.Maui.SegmentedControl, by introducing a Windows target framework and a first WinUI-based handler implementation, plus aligning the sample project’s Windows platform version settings.
Changes:
- Add a Windows target framework (
net10.0-windows10.0.19041.0) and Windows platform min/version properties to the plugin project. - Introduce a Windows
SegmentedControlHandlerimplemented with WinUIGrid+Buttons. - Enable Windows
SupportedOSPlatformVersion/TargetPlatformMinVersionsettings in the sample project.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Plugin.Maui.SegmentedControl/Plugin.Maui.SegmentedControl.csproj | Adds conditional Windows TFM and Windows platform version settings; includes Windows platform folder items. |
| src/Plugin.Maui.SegmentedControl/Platforms/Windows/SegmentedControlHandler.cs | New WinUI handler implementing segment rendering and interaction for Windows. |
| samples/SegmentedControlSamples/SegmentedControlSamples.csproj | Enables Windows supported/min platform versions for the sample app. |
…Segmented control Co-authored-by: yurkinh <17849938+yurkinh@users.noreply.github.com>
…csproj Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…che brushes, always include Windows TFM Co-authored-by: yurkinh <17849938+yurkinh@users.noreply.github.com>
| ResourceKey="SystemColorButtonFaceColor" /> | ||
| <StaticResource x:Key="SegmentedBorderBrush" | ||
| ResourceKey="SystemColorHighlightColorBrush" /> | ||
| <Thickness x:Key="SegmentedBorderThickness">1</Thickness> | ||
|
|
||
| <StaticResource x:Key="ButtonItemBackground" | ||
| ResourceKey="SystemColorButtonFaceColor" /> | ||
| <StaticResource x:Key="ButtonItemBackgroundPointerOver" | ||
| ResourceKey="SystemColorHighlightColor" /> | ||
| <StaticResource x:Key="ButtonItemBackgroundPressed" | ||
| ResourceKey="SystemColorHighlightColor" /> | ||
| <StaticResource x:Key="ButtonItemBackgroundSelected" | ||
| ResourceKey="SystemColorHighlightColor" /> | ||
| <StaticResource x:Key="ButtonItemBackgroundSelectedPointerOver" | ||
| ResourceKey="SystemColorHighlightColor" /> | ||
| <StaticResource x:Key="ButtonItemBackgroundSelectedPressed" | ||
| ResourceKey="SystemColorHighlightColor" /> | ||
| <StaticResource x:Key="ButtonItemBackgroundDisabled" | ||
| ResourceKey="SystemColorButtonFaceColor" /> | ||
|
|
||
| <StaticResource x:Key="ButtonItemForeground" | ||
| ResourceKey="SystemColorButtonTextColor" /> | ||
| <StaticResource x:Key="ButtonItemForegroundPointerOver" | ||
| ResourceKey="SystemColorHighlightTextColor" /> | ||
| <StaticResource x:Key="ButtonItemForegroundPressed" | ||
| ResourceKey="SystemColorHighlightTextColor" /> | ||
| <StaticResource x:Key="ButtonItemForegroundSelected" | ||
| ResourceKey="SystemColorHighlightTextColor" /> | ||
| <StaticResource x:Key="ButtonItemForegroundSelectedPointerOver" | ||
| ResourceKey="SystemColorHighlightTextColor" /> | ||
| <StaticResource x:Key="ButtonItemForegroundSelectedPressed" | ||
| ResourceKey="SystemColorHighlightTextColor" /> | ||
| <StaticResource x:Key="ButtonItemForegroundDisabled" | ||
| ResourceKey="SystemColorGrayTextColor" /> |
There was a problem hiding this comment.
The HighContrast theme dictionary aliases SystemColor*Color resources (eg SystemColorHighlightColor, SystemColorButtonFaceColor, SystemColorButtonTextColor) into keys that are later used for Background/Foreground (Brush) properties. In WinUI these *Color resources are Color values (not Brush), which can cause XAML type conversion/runtime failures. Use the corresponding *Brush resources (eg SystemColorHighlightColorBrush, SystemColorButtonFaceColorBrush, SystemColorButtonTextColorBrush, etc.) for brush-valued keys.
| ResourceKey="SystemColorButtonFaceColor" /> | |
| <StaticResource x:Key="SegmentedBorderBrush" | |
| ResourceKey="SystemColorHighlightColorBrush" /> | |
| <Thickness x:Key="SegmentedBorderThickness">1</Thickness> | |
| <StaticResource x:Key="ButtonItemBackground" | |
| ResourceKey="SystemColorButtonFaceColor" /> | |
| <StaticResource x:Key="ButtonItemBackgroundPointerOver" | |
| ResourceKey="SystemColorHighlightColor" /> | |
| <StaticResource x:Key="ButtonItemBackgroundPressed" | |
| ResourceKey="SystemColorHighlightColor" /> | |
| <StaticResource x:Key="ButtonItemBackgroundSelected" | |
| ResourceKey="SystemColorHighlightColor" /> | |
| <StaticResource x:Key="ButtonItemBackgroundSelectedPointerOver" | |
| ResourceKey="SystemColorHighlightColor" /> | |
| <StaticResource x:Key="ButtonItemBackgroundSelectedPressed" | |
| ResourceKey="SystemColorHighlightColor" /> | |
| <StaticResource x:Key="ButtonItemBackgroundDisabled" | |
| ResourceKey="SystemColorButtonFaceColor" /> | |
| <StaticResource x:Key="ButtonItemForeground" | |
| ResourceKey="SystemColorButtonTextColor" /> | |
| <StaticResource x:Key="ButtonItemForegroundPointerOver" | |
| ResourceKey="SystemColorHighlightTextColor" /> | |
| <StaticResource x:Key="ButtonItemForegroundPressed" | |
| ResourceKey="SystemColorHighlightTextColor" /> | |
| <StaticResource x:Key="ButtonItemForegroundSelected" | |
| ResourceKey="SystemColorHighlightTextColor" /> | |
| <StaticResource x:Key="ButtonItemForegroundSelectedPointerOver" | |
| ResourceKey="SystemColorHighlightTextColor" /> | |
| <StaticResource x:Key="ButtonItemForegroundSelectedPressed" | |
| ResourceKey="SystemColorHighlightTextColor" /> | |
| <StaticResource x:Key="ButtonItemForegroundDisabled" | |
| ResourceKey="SystemColorGrayTextColor" /> | |
| ResourceKey="SystemColorButtonFaceColorBrush" /> | |
| <StaticResource x:Key="SegmentedBorderBrush" | |
| ResourceKey="SystemColorHighlightColorBrush" /> | |
| <Thickness x:Key="SegmentedBorderThickness">1</Thickness> | |
| <StaticResource x:Key="ButtonItemBackground" | |
| ResourceKey="SystemColorButtonFaceColorBrush" /> | |
| <StaticResource x:Key="ButtonItemBackgroundPointerOver" | |
| ResourceKey="SystemColorHighlightColorBrush" /> | |
| <StaticResource x:Key="ButtonItemBackgroundPressed" | |
| ResourceKey="SystemColorHighlightColorBrush" /> | |
| <StaticResource x:Key="ButtonItemBackgroundSelected" | |
| ResourceKey="SystemColorHighlightColorBrush" /> | |
| <StaticResource x:Key="ButtonItemBackgroundSelectedPointerOver" | |
| ResourceKey="SystemColorHighlightColorBrush" /> | |
| <StaticResource x:Key="ButtonItemBackgroundSelectedPressed" | |
| ResourceKey="SystemColorHighlightColorBrush" /> | |
| <StaticResource x:Key="ButtonItemBackgroundDisabled" | |
| ResourceKey="SystemColorButtonFaceColorBrush" /> | |
| <StaticResource x:Key="ButtonItemForeground" | |
| ResourceKey="SystemColorButtonTextColorBrush" /> | |
| <StaticResource x:Key="ButtonItemForegroundPointerOver" | |
| ResourceKey="SystemColorHighlightTextColorBrush" /> | |
| <StaticResource x:Key="ButtonItemForegroundPressed" | |
| ResourceKey="SystemColorHighlightTextColorBrush" /> | |
| <StaticResource x:Key="ButtonItemForegroundSelected" | |
| ResourceKey="SystemColorHighlightTextColorBrush" /> | |
| <StaticResource x:Key="ButtonItemForegroundSelectedPointerOver" | |
| ResourceKey="SystemColorHighlightTextColorBrush" /> | |
| <StaticResource x:Key="ButtonItemForegroundSelectedPressed" | |
| ResourceKey="SystemColorHighlightTextColorBrush" /> | |
| <StaticResource x:Key="ButtonItemForegroundDisabled" | |
| ResourceKey="SystemColorGrayTextColorBrush" /> |
| <VisualStateGroup x:Name="DisabledStates"> | ||
| <VisualState x:Name="Enabled" /> | ||
| <VisualState x:Name="Disabled"> | ||
| <Storyboard> | ||
| <DoubleAnimation Storyboard.TargetName="PART_Root" | ||
| Storyboard.TargetProperty="Opacity" | ||
| To="{ThemeResource SegmentedItemDisabledOpacity}" | ||
| Duration="0:0:0.083" /> | ||
| </Storyboard> |
There was a problem hiding this comment.
The template uses a separate DisabledStates group (Enabled/Disabled) and the Disabled state only animates opacity. Control/ListViewItem commonly drives IsEnabled via a Disabled state inside CommonStates, so this group may not be entered automatically, and even if it is entered the handler-provided ButtonItemBackgroundDisabled/ButtonItemForegroundDisabled brushes aren’t applied anywhere. Consider handling disabled in CommonStates (or in code) and explicitly setting Background/Foreground to the disabled theme resources so DisabledBackgroundColor/DisabledTextColor take effect.
| var currentItem = ItemFromContainer(currentContainerItem); | ||
| var previousIndex = Items.IndexOf(currentItem); | ||
| var index = Math.Clamp(previousIndex + adjustment, 0, Items.Count); |
There was a problem hiding this comment.
In MoveFocus, the clamped upper bound uses Items.Count, which allows index == Items.Count and guarantees ContainerFromIndex(index) returns null. This breaks keyboard navigation when moving past the last item (and can also prevent moving focus to the last item in some cases). Clamp to Items.Count - 1 (and handle the empty-items case) so the computed index is always a valid item index.
| var currentItem = ItemFromContainer(currentContainerItem); | |
| var previousIndex = Items.IndexOf(currentItem); | |
| var index = Math.Clamp(previousIndex + adjustment, 0, Items.Count); | |
| if (Items.Count is 0) | |
| { | |
| return false; | |
| } | |
| var currentItem = ItemFromContainer(currentContainerItem); | |
| var previousIndex = Items.IndexOf(currentItem); | |
| var index = Math.Clamp(previousIndex + adjustment, 0, Items.Count - 1); |
| void UpdateVisualStates() | ||
| { | ||
| string contentState = (Icon is null, Content is null) switch | ||
| { | ||
| (false, false) => _isVertical ? IconTopState : IconLeftState, | ||
| (false, true) => IconOnlyState, | ||
| (true, false) => ContentOnlyState, | ||
| _ => ContentOnlyState, | ||
| }; | ||
|
|
||
| Microsoft.UI.Xaml.VisualStateManager.GoToState(this, contentState, true); | ||
| Microsoft.UI.Xaml.VisualStateManager.GoToState(this, _isVertical ? VerticalState : HorizontalState, true); |
There was a problem hiding this comment.
UpdateVisualStates() transitions to IconTop, Horizontal, and Vertical states, but the provided WinUI template in SegmentedStyles.xaml only defines IconLeft, IconOnly, and ContentOnly (and no orientation states). As a result, GoToState will fail for those missing states and vertical-orientation/icon+content layout won’t work. Either add the missing VisualStates to the template or remove/adjust the unreachable states in code to match the simplified template.
Platforms/Windows/NativeSegmented/Platforms/Windows/Themes/SegmentedStyles.xamlPlatforms/Windows/SegmentedControlHandler.csto use the extractedSegmentedcontrolAppHostBuilderExtensionsto load WinUI3 resource dictionary on WindowsPagebuild action for XAMLDisabledBackgroundColor→ButtonItemBackgroundDisabled(unselected disabled segment bg);DisabledTintColor→ border/outline when disabled;MapIsEnablednow toggles border betweenTintColor/DisabledTintColorSegmentedControlHandlerto avoid per-update allocationsEnableWindowsTargeting=trueso non-Windows CI hosts can compile the Windows TFM (TFM itself was already made unconditional in prior commit)💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.