Implementing a custom Checkbox control

Building a Custom Checkbox Control

This post is a code dump for a custom checkbox control.

There’s four main parts for this:

Custom Control
* Bindings
* XAML

Host Page
* XAML
* Viewmodel

Bindings

    public partial class CheckMark : ContentView
    {
        public CheckMark()
        {
            InitializeComponent();
        }

        public static readonly BindableProperty SelectionCommandProperty = 
            BindableProperty.Create("SelectionCommand", typeof(ICommand), typeof(CheckMark), null);

        public ICommand SelectionCommand
        {
            get { return (ICommand)GetValue(SelectionCommandProperty); }
            set { SetValue(SelectionCommandProperty, value); }
        }

        public static readonly BindableProperty CommandParameterProperty = 
            BindableProperty.Create("CommandParameter", typeof(object), typeof(CheckMark), null);

        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        public static BindableProperty BoxColorProperty = BindableProperty.Create(
                                                propertyName: "BoxColor",
                                                returnType: typeof(Color),
                                                declaringType: typeof(CheckMark),
                                                defaultValue: Color.Transparent,
                                                defaultBindingMode: BindingMode.TwoWay);

        public Color BoxColor
        {
            get { return (Color) GetValue(BoxColorProperty); }
            set
            {
                SetValue(BoxColorProperty, value);
            }
        }

        public static BindableProperty SelectedBoxColorProperty = BindableProperty.Create(
                                propertyName: "SelectedBoxColor",
                                returnType: typeof(Color),
                                declaringType: typeof(CheckMark),
                                defaultValue: Color.Transparent,
                                defaultBindingMode: BindingMode.TwoWay);

        public Color SelectedBoxColor
        {
            get { return (Color)GetValue(SelectedBoxColorProperty); }
            set
            {
                SetValue(SelectedBoxColorProperty, value);
            }
        }

        public static BindableProperty DeselectedBoxColorProperty = BindableProperty.Create(
                                        propertyName: "DeselectedBoxColor",
                                        returnType: typeof(Color),
                                        declaringType: typeof(CheckMark),
                                        defaultValue: Color.Transparent,
                                        defaultBindingMode: BindingMode.TwoWay);

        public Color DeselectedBoxColor
        {
            get { return (Color)GetValue(DeselectedBoxColorProperty); }
            set
            {
                SetValue(DeselectedBoxColorProperty, value);
            }
        }


        public static BindableProperty TextProperty = BindableProperty.Create(
                                        propertyName: "Text",
                                        returnType: typeof(string),
                                        declaringType: typeof(CheckMark),
                                        defaultValue: "",
                                        defaultBindingMode: BindingMode.TwoWay);

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set
            {
                SetValue(TextProperty, value);
            }
        }

        public static BindableProperty TextColorProperty = BindableProperty.Create(
                        propertyName: "TextColor",
                        returnType: typeof(Color),
                        declaringType: typeof(CheckMark),
                        defaultValue: Color.Black,
                        defaultBindingMode: BindingMode.TwoWay);

        public Color TextColor
        {
            get { return (Color)GetValue(TextColorProperty); }
            set
            {
                SetValue(TextColorProperty, value);
            }
        }

        public static BindableProperty DeselectedTextColorProperty = BindableProperty.Create(
            propertyName: "DeselectedTextColor",
            returnType: typeof(Color),
            declaringType: typeof(CheckMark),
            defaultValue: Color.Transparent,
            defaultBindingMode: BindingMode.TwoWay);


        public Color DeselectedTextColor
        {
            get { return (Color)GetValue(DeselectedTextColorProperty); }
            set
            {
                SetValue(DeselectedTextColorProperty, value);
            }
        }

        public static BindableProperty FontSizeProperty = BindableProperty.Create(
            propertyName: "FontSize",
            returnType: typeof(int),
            declaringType: typeof(CheckMark),
            defaultValue: 18,
            defaultBindingMode: BindingMode.TwoWay);

        public int FontSize
        {
            get { return (int)GetValue(FontSizeProperty); }
            set
            {
                SetValue(FontSizeProperty, value);
            }
        }

        public static BindableProperty SelectedTextColorProperty = BindableProperty.Create(
                propertyName: "SelectedTextColor",
                returnType: typeof(Color),
                declaringType: typeof(CheckMark),
                defaultValue: Color.Transparent,
                defaultBindingMode: BindingMode.TwoWay);

        public Color SelectedTextColor
        {
            get { return (Color)GetValue(SelectedTextColorProperty); }
            set
            {
                SetValue(SelectedTextColorProperty, value);
            }
        }

        public static BindableProperty SelectedBackgroundColorProperty = BindableProperty.Create(
            propertyName: "SelectedBackgroundColor",
            returnType: typeof(Color),
            declaringType: typeof(CheckMark),
            defaultValue: Color.Transparent,
            defaultBindingMode: BindingMode.TwoWay);

        public Color SelectedBackgroundColor
        {
            get { return (Color)GetValue(SelectedBackgroundColorProperty); }
            set
            {
                SetValue(SelectedBackgroundColorProperty, value);
            }
        }

        public static BindableProperty DeselectedBackgroundColorProperty = BindableProperty.Create(
            propertyName: "DeselectedBackgroundColor",
            returnType: typeof(Color),
            declaringType: typeof(CheckMark),
            defaultValue: Color.Transparent,
            defaultBindingMode: BindingMode.TwoWay);

        public Color DeselectedBackgroundColor
        {
            get { return (Color)GetValue(DeselectedBackgroundColorProperty); }
            set
            {
                SetValue(DeselectedBackgroundColorProperty, value);
            }
        }

        void CheckBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
        {
            if (e.Value)
            {
                BoxColor        = SelectedBoxColor;
                TextColor       = SelectedTextColor;
                BackgroundColor = SelectedBackgroundColor;
            }
            else
            {
                BoxColor        = DeselectedBoxColor;
                TextColor       = DeselectedTextColor;
                BackgroundColor = DeselectedBackgroundColor;
            }

            object resolvedParameter;

            if (CommandParameter != null)
                 resolvedParameter = CommandParameter;
            else resolvedParameter = e;

            if (SelectionCommand?.CanExecute(resolvedParameter) ?? true)
                SelectionCommand?.Execute(resolvedParameter);
        }

        void TapGestureRecognizer_Tapped(object sender, EventArgs e)
        {
            CheckboxControl.IsChecked = !CheckboxControl.IsChecked;
        }
    }

XAML

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             
             x:Name="ParentControl"
             x:Class="Temp.CheckMark">

    <Grid HorizontalOptions="Fill" BackgroundColor="Transparent" >

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Grid.GestureRecognizers>
            <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
        </Grid.GestureRecognizers>

        <Label  Grid.Column="0"
                
                Text="{Binding Source={x:Reference ParentControl}, Path=Text}" 
                TextColor="{Binding Source={x:Reference ParentControl}, Path= TextColor, Mode=TwoWay}" 
                FontSize ="{Binding Source={x:Reference ParentControl}, Path= FontSize,  Mode=TwoWay}" 
                BackgroundColor="Transparent" 
                HorizontalTextAlignment="Center"
                VerticalOptions="CenterAndExpand" 
                HorizontalOptions="Fill" />

        <CheckBox Grid.Column="0"
                  x:Name="CheckboxControl"
                  Color="{Binding Source={x:Reference ParentControl}, Path=BoxColor, Mode=TwoWay}"
                  BackgroundColor="Transparent"
                  VerticalOptions="Center" 
                  HorizontalOptions="End"
                  CheckedChanged="CheckBox_CheckedChanged" />

    </Grid>

</ContentView>
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             
             x:Name="ParentControl"
             x:Class="Temp.CheckMark">

    <Grid HorizontalOptions="Fill" BackgroundColor="Transparent" >

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Grid.GestureRecognizers>
            <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
        </Grid.GestureRecognizers>

        <Label  Grid.Column="0"
                
                Text="{Binding Source={x:Reference ParentControl}, Path=Text}" 
                TextColor="{Binding Source={x:Reference ParentControl}, Path= TextColor, Mode=TwoWay}" 
                FontSize ="{Binding Source={x:Reference ParentControl}, Path= FontSize,  Mode=TwoWay}" 
                BackgroundColor="Transparent" 
                HorizontalTextAlignment="Center"
                VerticalOptions="CenterAndExpand" 
                HorizontalOptions="Fill" />

        <CheckBox Grid.Column="0"
                  x:Name="CheckboxControl"
                  Color="{Binding Source={x:Reference ParentControl}, Path=BoxColor, Mode=TwoWay}"
                  BackgroundColor="Transparent"
                  VerticalOptions="Center" 
                  HorizontalOptions="End"
                  CheckedChanged="CheckBox_CheckedChanged" />

    </Grid>

</ContentView>

Host Page

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             xmlns:local="clr-namespace:Temp"
             x:Class="Temp.MainPage">
    
    

    <ContentPage.BindingContext>
        <local:Viewmodel />
    </ContentPage.BindingContext>

    <local:CheckMark   Text="{Binding UserName}"
                        
                        SelectionCommand="{Binding MyCommand}"
                        
                        BoxColor                  ="Black"
                        BackgroundColor           ="Pink"
                                                  
                        SelectedBoxColor          ="Yellow"
                        DeselectedBoxColor        ="Pink"
                                                  
                        SelectedTextColor         ="White"
                        SelectedBackgroundColor   ="Green"
                                                  
                        DeselectedTextColor       ="Black"
                        DeselectedBackgroundColor ="Red"
                        
                        FontSize                  ="26"
                        WidthRequest              ="450"
                        HeightRequest             ="100"
                        />

</ContentPage>

Viewmodel

    public class Viewmodel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public Viewmodel()
        {
            MyCommand = new DelegateCommand(arg => Debug.WriteLine("Hello World"));
        }     

        public ICommand MyCommand { get; set; }

        string _username = "default-username";
        public string UserName 
        {
            get { return _username; }
            set
            {
                if (_username != value)
                {
                    _username = value;
                    OnPropertyChanged();
                }
            } 
        }

        void OnPropertyChanged([CallerMemberName] string propertyname = "")
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
        }
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: