dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.27k stars 1.76k forks source link

CheckBox does not support ControlTemplates #18094

Open jkhoel opened 1 year ago

jkhoel commented 1 year ago

Description

I am unable to assign control templates to CheckBox elements. When looking at the code, it seems it has no ControlTemplate property and no OnControlTemplateChanged() override when comparing to a RadioButton for example.

I was expecting to be able to use the same principle to style or modify these components similarly, as they are both essentially buttons with different styling and slightly different behaviour.

Steps to Reproduce

  1. Follow steps in the documentation for Control Templates
  2. Try using a control template on a CheckBox inside XAML

Link to public reproduction project repository

No response

Version with bug

7.0.96

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

iOS, Android, I was not able test on other platforms

Affected platform versions

No response

Did you find any workaround?

My workaround so far has been to completely ditch the MAUI CheckBox component completely, and implement my own.

The idea is to use custom visual states and then use these to toggle different SVGs (images) on and off.

Technically - In this particular case - could just ditch the Visual styles pattern and use the IsVisible property built into the Image elements. But I want a reusable pattern, and I could use the same pattern for changing any property on the elements - for example change the color of a label based on some event or value.

MyCheckBox.xaml

<?xml version="1.0" encoding="utf-8"?>

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="SomeProject.Views.MyStyledCheckBox">

            <FlexLayout x:Name="Container" Position="Absolute">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup Name="CheckedStates">
                        <VisualState x:Name="Unchecked">
                            <VisualState.Setters>
                                <Setter TargetName="DefaultCheckBox" Property="Image.IsVisible" Value="True" />
                                <Setter TargetName="SelectedCheckBox" Property="Image.IsVisible" Value="False" /> 
                            </VisualState.Setters>
                        </VisualState>
                        <VisualState x:Name="Checked">
                            <VisualState.Setters>
                                <Setter TargetName="DefaultCheckBox" Property="Image.IsVisible" Value="False" />
                                <Setter TargetName="SelectedCheckBox" Property="Image.IsVisible" Value="True" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>

                <FlexLayout.GestureRecognizers>
                    <TapGestureRecognizer Tapped="ToggleCheckBox" />
                </FlexLayout.GestureRecognizers>

                <StackLayout Margin="0, 0, 8, 0">
                    <Image x:Name="DefaultCheckBox" WidthRequest="24" HeightRequest="24" Source="checkbox_default" />
                    <Image x:Name="SelectedCheckBox" WidthRequest="24" HeightRequest="24" Source="checkbox_selected" />
                </StackLayout>

            </FlexLayout>
</ContentView>

MyCheckBox.xaml.cs

public partial class MyCheckBox
{
    private bool IsChecked { get; set; }

    public UserConsentCheckBox()
    {
        InitializeComponent();
        VisualStateManager.GoToState(Container, nameof("Unchecked"));
    }

    public void SwitchState(bool isChecked) => VisualStateManager.GoToState(this, isChecked ? "Unchecked" : "Checked");

    private void ToggleCheckBox(object sender, TappedEventArgs e)
    {
        VisualStateManager.GoToState(Container, IsChecked ? "Unchecked" : "Checked");
        IsChecked = !IsChecked;
    }
}

Relevant log output

No response

ghost commented 1 year ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

heribertolugo commented 3 months ago

Ran into this issue as well. Initially I was trying to use a switch. But when "off" the switch is not visible in dark mode, only the thumb is visible - so it just looks like a random circle on the screen (Windows, not tested on mobile). Changing background color for switch is not an option, it just makes a huge colored rectangle behind the switch - completely breaking the aesthetics. This switch issue has been mentioned here, here and here in addition to multiple posts on stackoverflow.

Decided to mimic the switch using icons and a CheckBox through ControlTemplate. Cannot. Thought I would just go back to the switch with a ControlTemplate. Cannot. Definitely did not want to explore the probable time consuming path of using shapes in a custom control to display a checked state in a pleasing manner.

Purpose of my post is:

Many hours spent on trying to implement something which should be trivial, much on research. Dark mode is advertised but seems to cause many issues with controls that have properties lockdown. Guess I'll have to go down the path with VisualStateManager and/or implement another pseudo-custom control.

peruchali commented 3 months ago

It doesn't seem consistent for RadioButton to have ControlTemplate but not CheckBox. Is there any technical difficulty why it's not supported for CheckBox?