Search code examples
c#xamarin.formsrenderer

Button BorderColor gradient not supporting run time color change?


By using a custom renderer one can create a gradient color effect on the Border of a Button in Xamarin.Forms. Done by editing the BorderColor property in OnElementChanged override method. [Gradient Button credits to @Nico Zhu]

Currently the buttons gradient works on initial start-up. When the border (or StartColor) are changed during run time, the gradient is masked over. See results below. The first is the gradient as shown on load, second shows whats currently selected, and the third is a previously selected button which reverted its border color back to black, but again this still is masking over the initial gradient.

How to support run time changes and maintain the gradient Border on the XF Button?

states of color

Xamarin.Forms Gradient Button

using Xamarin.Forms;

namespace XamarinGradientButtonTest
{
    public class GradientButton : Button
    {
        public static readonly BindableProperty StartColorProperty = BindableProperty.Create(
    propertyName: "StartColor",
    returnType: typeof(Color),
    declaringType: typeof(GradientButton),
    defaultValue: default(Color));

        public Color StartColor
        {
            get { return (Color)GetValue(StartColorProperty); }
            set { SetValue(StartColorProperty, value); }
        }

    }
}

iOS Renderer

using System;
using CoreAnimation;
using CoreGraphics;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using XamarinGradientButtonTest;
using XamarinGradientButtonTest.iOS;

[assembly: ExportRenderer(typeof(GradientButton), typeof(GradientButtonRenderer))]

namespace XamarinGradientButtonTest.iOS
{
    public class GradientButtonRenderer : ButtonRenderer
    {
        CAGradientLayer gradient;
        CAShapeLayer shape;
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);

            gradient = new CAGradientLayer();
            gradient.Colors = new CGColor[] { ((GradientButton)Element).StartColor.ToCGColor(), Element.BorderColor.ToCGColor() };

            shape = new CAShapeLayer();
            shape.LineWidth = (nfloat)(Element.BorderWidth);
            shape.StrokeColor = UIColor.Black.CGColor;
            shape.FillColor = UIColor.Clear.CGColor;
            gradient.Mask = shape;

            Control.Layer.AddSublayer(gradient);
            Control.Layer.BorderColor = UIColor.Clear.CGColor;
        }

        public override void Draw(CGRect rect)
        {
            base.Draw(rect);

            shape.Path = UIBezierPath.FromRect(rect).CGPath;
            gradient.Frame = rect;
        }
    }
}

XF Button Creation

<local:GradientButton BorderColor="Black" BorderRadius="5" StartColor="White" BorderWidth="8" WidthRequest="50" HeightRequest="44" VerticalOptions="Start" HorizontalOptions="Start"/>

On Click

private void btnClick(object sender, EventArgs e)
{
    ((GradientButton)sender).BorderColor = Color.Green;
}

On De-Select

BorderColor = Color.Black;

Solution

  • You can set the borderColor in the method OnElementPropertyChanged.Add the following code in GradientButtonRenderer

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
     {
        base.OnElementPropertyChanged(sender, e);
    
        if(e.PropertyName=="BorderColor")
         {
            var color = (Element as GradientButton).BorderColor;
            gradient.Colors = new CGColor[] { ((GradientButton)Element).StartColor.ToCGColor(), color.ToCGColor() };
    
            shape.StrokeColor = color.ToCGColor();
            shape.FillColor = UIColor.Clear.CGColor;
            gradient.Mask = shape;
    
            Control.Layer.AddSublayer(gradient);
            Control.Layer.BorderColor = UIColor.Clear.CGColor;
    
         }
    
    }