How to implement and use SfRadioButtonRenderer in NET MAUI

How can I implement SfRadioButtonRenderer in net maui, I want to migrate this code from Xamarin Forms:

Shared

namespace MyApp.Controls.Renderers
{
    public class CustomSfRadioButton : SfRadioButton
    {
        public event EventHandler<EventArgs> Pressed;
        public event EventHandler<EventArgs> UnPressed;

        public static readonly BindableProperty RippleColorProperty =
            BindableProperty.Create(nameof(RippleColor), typeof(Color), typeof(CustomSfRadioButton), Color.Gray);

        public Color RippleColor
        {
            get => (Color)GetValue(RippleColorProperty);
            set => SetValue(RippleColorProperty, value);
        }

        public static readonly BindableProperty ValueProperty =
            BindableProperty.Create(nameof(Value), typeof(int), typeof(CustomSfRadioButton), 0);

        public int Value
        {
            get => (int)GetValue(ValueProperty);
            set => SetValue(ValueProperty, value);
        }

        public void PressedEvent()
        {
            Pressed?.Invoke(this, EventArgs.Empty);
        }

        public void UnPressedEvent()
        {
            UnPressed?.Invoke(this, EventArgs.Empty);
        }
    }
}


Android

[assembly: ExportRenderer(typeof(CustomSfRadioButton), typeof(CustomSfRadioButtonRenderer))]
namespace MyApp.Droid.Renderers
{
    public class CustomSfRadioButtonRenderer : SfRadioButtonRenderer
    {
        public CustomSfRadioButtonRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<SfRadioButton> e)
        {
            base.OnElementChanged(e);

            if (Control == null || e.NewElement == null) return;

            SetRippleColor();

            var compoundButtonDrawable = CompoundButtonCompat.GetButtonDrawable(Control);
            var insetDrawable = new InsetDrawable(compoundButtonDrawable, 32, 0, 0, 0);
            Control.SetButtonDrawable(insetDrawable);
            Control.SetPadding(40, 0, 0, 0);
            Control.SetValue(CustomSfRadioButton.ValueProperty, ((CustomSfRadioButton)e.NewElement).Value);
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == CustomSfRadioButton.RippleColorProperty.PropertyName)
            {
                SetRippleColor();
            }
            else if (e.PropertyName == CustomSfRadioButton.ValueProperty.PropertyName)
            {
                Control.SetValue(CustomSfRadioButton.ValueProperty, ((CustomSfRadioButton)Element)?.Value);
            }
        }

        private void SetRippleColor()
        {
            var customRadioButton = (CustomSfRadioButton)Element;
            var color = customRadioButton.RippleColor.ToAndroid();
            const int nuevoRadio = 1500;

            RippleDrawable rippleDrawable;

       
                rippleDrawable = new RippleDrawable(ColorStateList.ValueOf(color), null, new ColorDrawable(color))
                {
                    Radius = nuevoRadio
                };
        }

        public override bool DispatchTouchEvent(MotionEvent e)
        {
            if (((CustomSfRadioButton)Element).InputTransparent)
            {
                return true;
            }

            switch (e.Action)
            {
                case MotionEventActions.Down:
                    ((CustomSfRadioButton)Element).PressedEvent();
                    break;
                case MotionEventActions.Up:
                case MotionEventActions.Cancel:
                    ((CustomSfRadioButton)Element).UnPressedEvent();
                    break;
            }

            return base.DispatchTouchEvent(e);
        }
    }
}

3 Replies

BV Brundha Velusamy Syncfusion Team April 15, 2024 01:16 PM UTC

Hi Eyner,

 

To ensure we provide better assistance, we need clarification on your requirements regarding the provided code snippet. While you have customized the SfRadioButton by initializing the Pressed and UnPressed events, as well as the RippleColor and Value properties, it appears that the RippleColor implementation in the custom renderer is incomplete. Could you please explain the exact role of the RippleColor property? Additionally, could you provide more details about the Value property? This information will enable us to offer a prompt solution. Thank you for your cooperation.

 

Regards,

Brundha V



EY eyner April 15, 2024 02:46 PM UTC

The RippleColor is to change the background color when the SfRadioButton is pressed, the Value is to give you an Id to identify which SfRadioButton is selecting. In addition, the Pressed and Unpressed events are so that multiple SfRadioButtons cannot be selected, thus preventing the RippleColor Background from being active on multiple SfRadioButtons, to handle that I have a behavior


xaml

<buttons:SfRadioGroup CheckedChanged="SfRadioGroup_OnCheckedChanged" Spacing="0">
                                <renderers:CustomSfRadioButton
                                    x:Name="Op1"
                                    HeightRequest="50"
                                    RippleColor="{DynamicResource Gray-Ripple}"
                                    Text="Mode Light"
                                    TextColor="{DynamicResource Gray-900}"
                                    UncheckedColor="Gray"
                                    Value="1">
                                    <renderers:CustomSfRadioButton.Behaviors>
                                        <behaviors:RadioButtonBehavior />
                                    </renderers:CustomSfRadioButton.Behaviors>
                                </renderers:CustomSfRadioButton>
                                <BoxView
                                    Margin="18,0,0,0"
                                    HeightRequest="0.1"
                                    Color="{DynamicResource Gray-200}" />
                                <renderers:CustomSfRadioButton
                                    x:Name="Op2"
                                    HeightRequest="50"
                                    RippleColor="{DynamicResource Gray-Ripple}"
                                    Text="Mode dark"
                                    TextColor="{DynamicResource Gray-900}"
                                    UncheckedColor="Gray"
                                    Value="2">
                                    <renderers:CustomSfRadioButton.Behaviors>
                                        <behaviors:RadioButtonBehavior />
                                    </renderers:CustomSfRadioButton.Behaviors>
                                </renderers:CustomSfRadioButton>
                                <BoxView
                                    Margin="18,0,0,0"
                                    HeightRequest="0.1"
                                    Color="{DynamicResource Gray-200}" />
                                <renderers:CustomSfRadioButton
                                    x:Name="Op3"
                                    HeightRequest="50"
                                    RippleColor="{DynamicResource Gray-Ripple}"
                                    Text="Default system"
                                    TextColor="{DynamicResource Gray-900}"
                                    UncheckedColor="Gray"
                                    Value="3">
                                    <renderers:CustomSfRadioButton.Behaviors>
                                        <behaviors:RadioButtonBehavior />
                                    </renderers:CustomSfRadioButton.Behaviors>
                                </renderers:CustomSfRadioButton>
                            </buttons:SfRadioGroup>

<buttons:SfButton
                                x:Name="SfButtonSave"
                                Clicked="SaveTheme_OnClicked"
                                CornerRadius="5"
                                FontAttributes="Bold"
                                HeightRequest="45"
                                Text="Save">
                            </buttons:SfButton>


CodeBehind

public int ThemeTempChecked { get; set; }

private void SfRadioGroup_OnCheckedChanged(object sender, CheckedChangedEventArgs e)
        {
            var customRadioButton = (CustomSfRadioButton)e.CurrentItem;
            ThemeTempChecked = customRadioButton.Value;
        }

        private async void SaveTheme_OnClicked(object sender, EventArgs e)
        {
            switch (ThemeTempChecked)
            {
                case 1:
                    Application.Current.Resources.ApplyLightTheme();
                    break;
                case 2:
                    Application.Current.Resources.ApplyDarkTheme();
                    break;
                case 3:
                    Application.Current.Resources.ApplyDefaultTheme();

                    break;
            }
        }


Behavior

namespace MyApp.Behaviors
{
    public class RadioButtonBehavior : Behavior<CustomSfRadioButton>
    {
        private static readonly List<CustomSfRadioButton> RadioButtons = new List<CustomSfRadioButton>();

        protected override void OnAttachedTo(CustomSfRadioButton radioButton)
        {
            RadioButtons.Add(radioButton);
            radioButton.Pressed += OnPressed;
            radioButton.UnPressed += OnUnPressed;
            base.OnAttachedTo(radioButton);
        }

        protected override void OnDetachingFrom(CustomSfRadioButton radioButton)
        {
            RadioButtons.Remove(radioButton);
            radioButton.Pressed -= OnPressed;
            radioButton.UnPressed -= OnUnPressed;
            base.OnDetachingFrom(radioButton);
        }

        private static void OnPressed(object sender, EventArgs e)
        {
            var radioButton = (CustomSfRadioButton)sender;
            foreach (var sibling in RadioButtons)
            {
                if (sibling != radioButton)
                {
                    sibling.InputTransparent = true;
                }
            }
        }

        private static void OnUnPressed(object sender, EventArgs e)
        {
            var radioButton = (CustomSfRadioButton)sender;
            foreach (var sibling in RadioButtons)
            {
                if (sibling != radioButton)
                {
                    sibling.InputTransparent = false;
                }
            }
        }
    }
}


The final code of RippleColor in CustomSfRadioButtonRenderer SetRippleColor is

        private void SetRippleColor()
        {
            var customRadioButton = (CustomSfRadioButton)Element;
            var color = customRadioButton.RippleColor.ToAndroid();
            const int nuevoRadio = 1500;

            RippleDrawable rippleDrawable;

                rippleDrawable = new RippleDrawable(ColorStateList.ValueOf(color), null, new ColorDrawable(color))
                {
                    Radius = nuevoRadio
                };

            Control.Background = rippleDrawable;
        }



BV Brundha Velusamy Syncfusion Team April 18, 2024 03:15 PM UTC

Hi Eyner


After analyzing the provided code snippets, we have prepared a sample to fulfill your requirement in .NET MAUI SfRadioButton, similar to Xamarin SfRadioButton behavior. In the sample, we have extended the SfRadioButton class and programmatically triggered the SfEffectsViews effect to apply RippleBackground to the SfRadioButton.


Here's the code snippet for your reference,

//Custom Control

#if ANDROID

    public class CustomSfRadioButton : SfRadioButton, ITouchListener

#else

    public class CustomSfRadioButton : SfRadioButton

#endif

    {

        public CustomSfRadioButton()

        {

           

        }

#if ANDROID

        void ITouchListener.OnTouch(Syncfusion.Maui.Core.Internals.PointerEventArgs e)

        {                   

            var effects = this.Parent as SfEffectsView;

            if(e.Action == PointerActions.Pressed)

            {

                if (this.Parent is SfEffectsView)

                {

                    effects?.OnTouch(e);

                    this.IsChecked = true;

                }

            }

            else if(e.Action == PointerActions.Released)

            {

                if (this.Parent is SfEffectsView)

                {

                    effects?.OnTouch(e);                  

                }

            }                                                                  

        }

#endif

    }

 

//Xaml

<ContentPage.Resources>

    <buttons:SfRadioGroupKey x:Key="radioButton"/>

</ContentPage.Resources>

 

<ScrollView>

    <VerticalStackLayout

        Padding="30,0"

        Spacing="25">

        <buttons:SfRadioGroup x:Name="radioGroup">

           

            <core:SfEffectsView RippleBackground="Red">

                <local:CustomSfRadioButton Text="Mode Light"  GroupKey="{StaticResource radioButton}" />

            </core:SfEffectsView>

 

            <core:SfEffectsView RippleBackground="Red">

                <local:CustomSfRadioButton Text="Mode dark" GroupKey="{StaticResource radioButton}"/>

            </core:SfEffectsView>

 

            <core:SfEffectsView RippleBackground="Red">

                <local:CustomSfRadioButton  Text="Default system"  GroupKey="{StaticResource radioButton}"/>

            </core:SfEffectsView>

 

        </buttons:SfRadioGroup>

                  

    </VerticalStackLayout>

</ScrollView>


Please review the attached sample for reference. Feel free to share any concerns or feedback you may have regarding the implementation.


Regards,

Brundha V


Attachment: RadioButtonSample_cd046f18.zip

Loader.
Up arrow icon