Issues with styling a radio button in .Net MAUI
Asked Answered
A

3

5

I am running into some issues with styling a radio button in .Net MAUI. Initially, I noticed the the radio button look-and-feel is not consistent across Windows and Android, as can be seen in the image below:

Radio button look-and-feel

Not only are they different, but the available options for styling the button are limited. The only options for defining colors are "BorderColor", "BackgroundColor", and "TextColor". Both "BorderColor" and "BackgroundColor" have absolutely no effect on the color of the radio button itself. I'd like to change the color of the actual radio button.

So I decided to create a control template to help me out. Here is my control template:

<ControlTemplate x:Key="RadioButtonTemplate">
    <Border Stroke="Transparent" BackgroundColor="Transparent">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroupList>
                <VisualStateGroup x:Name="CheckedStates">
                    <VisualState x:Name="Checked">
                        <VisualState.Setters>
                            <Setter TargetName="check" Property="Opacity" Value="1" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Unchecked">
                        <VisualState.Setters>
                            <Setter TargetName="check" Property="Opacity" Value="0" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateGroupList>
        </VisualStateManager.VisualStateGroups>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid WidthRequest="20" HeightRequest="20" Grid.Column="0" VerticalOptions="Center" HorizontalOptions="Center">
                <Ellipse x:Name="border_circle" StrokeThickness="2" Stroke="DarkBlue" Fill="White" WidthRequest="18" HeightRequest="18" HorizontalOptions="Center" VerticalOptions="Center" />
                <Ellipse x:Name="check" Fill="DarkBlue" WidthRequest="10" HeightRequest="10" HorizontalOptions="Center" VerticalOptions="Center" />
            </Grid>
            <ContentPresenter Margin="10,0,0,0" Grid.Column="1" HorizontalOptions="Start" VerticalOptions="Center" />
        </Grid>
    </Border>
</ControlTemplate>

<Style TargetType="RadioButton" x:Key="RadioButtonStyle">
    <Setter Property="ControlTemplate" Value="{StaticResource RadioButtonTemplate}" />
</Style>

This worked reasonably well, so now I get the following look-and-feel on both Android and Windows:

Updated radio button look-and-feel

Now I have one last problem. When I was using the default radio buttons (without my control template), they would reliably turn a "light gray" color whenever I set IsEnabled to false. I'd like to make it so that I can disable my radio button and have it turn a light gray color (to indicate that it is disabled) while still using my control template so I have a unified look-and-feel.

So I attempted to add a Disabled visual state to my control template, but it doesn't seem to be working. Here is my new control template with a Disabled state:

<ControlTemplate x:Key="RadioButtonTemplate">
    <Border Stroke="Transparent" BackgroundColor="Transparent">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroupList>
                <VisualStateGroup x:Name="CheckedStates">
                    <VisualState x:Name="Checked">
                        <VisualState.Setters>
                            <Setter TargetName="check" Property="Opacity" Value="1" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Unchecked">
                        <VisualState.Setters>
                            <Setter TargetName="check" Property="Opacity" Value="0" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Disabled">
                        <VisualState.Setters>
                            <Setter TargetName="border_circle" Property="Stroke" Value="LightGray" />
                            <Setter TargetName="check" Property="Fill" Value="LightGray" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateGroupList>
        </VisualStateManager.VisualStateGroups>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid WidthRequest="20" HeightRequest="20" Grid.Column="0" VerticalOptions="Center" HorizontalOptions="Center">
                <Ellipse x:Name="border_circle" StrokeThickness="2" Stroke="DarkBlue" Fill="White" WidthRequest="18" HeightRequest="18" HorizontalOptions="Center" VerticalOptions="Center" />
                <Ellipse x:Name="check" Fill="DarkBlue" WidthRequest="10" HeightRequest="10" HorizontalOptions="Center" VerticalOptions="Center" />
            </Grid>
            <ContentPresenter Margin="10,0,0,0" Grid.Column="1" HorizontalOptions="Start" VerticalOptions="Center" />
        </Grid>
    </Border>
</ControlTemplate>

<Style TargetType="RadioButton" x:Key="RadioButtonStyle">
    <Setter Property="ControlTemplate" Value="{StaticResource RadioButtonTemplate}" />
</Style>

Unfortunately, it's not working. Not only does the color not change when the radio button is disabled, but also my 2nd setter (setting the "Fill" property of the "check" object) causes a compile-time error as well ("Cannot resolve property Fill on type Border").

Any suggestions?

Acerbate answered 20/1, 2023 at 23:6 Comment(2)
Instead of using workarounds try out github.com/FreakyAli/Maui.FreakyControlsInsolvable
I would prefer that the MAUI RadioButton have parity with the Checkbox. A 'Color' field should be available. The BorderColor draws a rectangle around the item on iOS, very disorienting. BackgroundColor does not have the desired effect. Currently I'm using TextColor to give at least some styling for RadioButton.Shawn
A
11

I was able to figure out a way to do what I needed to do. Rather than use the "Disabled" visual state in the visual state group, I was able to use style triggers. So now my control template and its corresponding style look like this:

<ControlTemplate x:Key="RadioButtonTemplate">
    <Border Stroke="Transparent" BackgroundColor="Transparent">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroupList>
                <VisualStateGroup x:Name="CheckedStates">
                    <VisualState x:Name="Checked">
                        <VisualState.Setters>
                            <Setter TargetName="check" Property="Opacity" Value="1" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Unchecked">
                        <VisualState.Setters>
                            <Setter TargetName="check" Property="Opacity" Value="0" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateGroupList>
        </VisualStateManager.VisualStateGroups>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid WidthRequest="20" HeightRequest="20" Grid.Column="0" VerticalOptions="Center" HorizontalOptions="Center">
                <Ellipse x:Name="border_circle" StrokeThickness="2" Stroke="{TemplateBinding BorderColor}" Fill="White" WidthRequest="18" HeightRequest="18" HorizontalOptions="Center" VerticalOptions="Center" />
                <Ellipse x:Name="check" Fill="{TemplateBinding BorderColor}" WidthRequest="10" HeightRequest="10" HorizontalOptions="Center" VerticalOptions="Center" />
            </Grid>
            <ContentPresenter Margin="10,0,0,0" Grid.Column="1" HorizontalOptions="Start" VerticalOptions="Center" />
        </Grid>
    </Border>
</ControlTemplate>

<Style TargetType="RadioButton" x:Key="RadioButtonStyle">
    <Setter Property="ControlTemplate" Value="{StaticResource RadioButtonTemplate}" />
    <Style.Triggers>
        <Trigger TargetType="RadioButton" Property="IsEnabled" Value="False">
            <Setter Property="BorderColor" Value="LightGray" />
        </Trigger>
        <Trigger TargetType="RadioButton" Property="IsEnabled" Value="True">
            <Setter Property="BorderColor" Value="DarkBlue" />
        </Trigger>
    </Style.Triggers>
</Style>
Acerbate answered 20/1, 2023 at 23:39 Comment(0)
B
1

In the VisualState.Setters prefix the property, e.g. Opacity of the element to set, e.g. Ellipse:

<VisualState x:Name="Checked">
  <VisualState.Setters>
    <Setter TargetName="border_circle" Property="Ellipse.Stroke" Value="{TemplateBinding BorderColor}" />
    <Setter TargetName="check" Property="Ellipse.Opacity" Value="1" />
 </VisualState.Setters>
</VisualState>
Byrle answered 27/10, 2023 at 9:37 Comment(0)
A
0

You can add this code in CreateMauiApp. This code will change the color of the RadioButton

Microsoft.Maui.Handlers.RadioButtonHandler.Mapper.AppendToMapping("MyCustomization", (handler, view) =>
            {
#if ANDROID
                if (handler.PlatformView is Android.Widget.RadioButton radioButton)
                {
                    var states = new int[][]
                    {
                        new int[] { Android.Resource.Attribute.StateChecked }, 
                        new int[] { -Android.Resource.Attribute.StateChecked }  
                    };

                    // Define the colors corresponding to the states (border color)
                    var colors = new int[]
                    {
                        Android.Graphics.Color.Orange,    
                        Android.Graphics.Color.Orange    
                    };

                    // Create a ColorStateList with the states and colors
                    var colorStateList = new Android.Content.Res.ColorStateList(states, colors);

                    // Apply the color tint to the button's border
                    radioButton.ButtonTintList = colorStateList;
                }
#endif
            }); 
Ashleyashli answered 8/10, 2024 at 13:7 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.