Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Help needed making a custom frame control with SkiaSharp

CaseCase USMember ✭✭✭

I want to make a new frame control, that is just like the Xamarin Forms Frame control. I want to use SkiaSharp Canvas as the background so I can draw different things in the background of the frame.

To start, I just want to replicate the default Frame class but allow a gradient fill. The problem I am having is how to implement the "Frame" part of it, meaning, how to add the content view. I have the gradient working beautifully.

In xaml, I would use it like this:

    <custom:GradientFrame  VerticalOptions="FillAndExpand"
                        HorizontalOptions="FillAndExpand"
                        OuterBackgroundColor="Transparent"
                        InnerBackgroundColorStart="DarkBlue"
                        InnerBackgroundColorEnd="LightBlue"
                        BorderColor="Black"
                        BorderWidth="2"
                        BorderRadius="25" 
                        FillOrientation="Vertical">

        <StackLayout Orientation="Vertical">

            <Label Text="Sample Text 1" />

            <Label Text="Sample Text 2" />

        </StackLayout>

    </custom:GradientFrame >

Since SkiaSharp is cross platform, I shouldn't need to write a custom renderer. I can use a SKCanvasView to draw my background frame, but not sure how to add the child view, i.e. the frame content.

Can someone point me in the right direction?

Here's my control so far ...

public class GradientFrame : SKCanvasView
{
    #region Outer
    public static readonly BindableProperty OuterBackgroundColorProperty =
        BindableProperty.Create("OuterBackgroundColor", 
            typeof(Color), 
            typeof(GradientFrame), 
            Color.Green, 
            propertyChanged: (currentControl, oldValue, newValue) =>
            {
                var thisControl = currentControl as GradientFrame;
                thisControl.OuterBackgroundColor = (Color)newValue;
            });

    public Color OuterBackgroundColor
    {
        get { return (Color)GetValue(OuterBackgroundColorProperty); }
        set
        {
            SetValue(OuterBackgroundColorProperty, value);
            thisOuterBackgroundColor = value.ToSKColor();
            InvalidateSurface();
        }
    }

    private SKColor thisOuterBackgroundColor;
    #endregion

    #region Inner Start
    public static readonly BindableProperty InnerBackgroundColorStartProperty =
        BindableProperty.Create("InnerBackgroundColorStart", 
            typeof(Color), 
            typeof(GradientFrame),
            Color.Red,
            propertyChanged: (currentControl, oldValue, newValue) =>
            {
                var thisControl = currentControl as GradientFrame;
                thisControl.InnerBackgroundColorStart = (Color)newValue;
            });

    public Color InnerBackgroundColorStart
    {
        get { return (Color)GetValue(InnerBackgroundColorStartProperty); }
        set
        {
            SetValue(InnerBackgroundColorStartProperty, value);
            thisInnerBackgroundColorStart = value.ToSKColor();
            InvalidateSurface();
        }
    }

    private SKColor thisInnerBackgroundColorStart;
    #endregion

    #region Inner End
    public static readonly BindableProperty InnerBackgroundColorEndProperty =
        BindableProperty.Create("InnerBackgroundColorEnd",
            typeof(Color),
            typeof(GradientFrame),
            Color.Red,
            propertyChanged: (currentControl, oldValue, newValue) =>
            {
                var thisControl = currentControl as GradientFrame;
                thisControl.InnerBackgroundColorEnd = (Color)newValue;
            });

    public Color InnerBackgroundColorEnd
    {
        get { return (Color)GetValue(InnerBackgroundColorEndProperty); }
        set
        {
            SetValue(InnerBackgroundColorEndProperty, value);
            thisInnerBackgroundColorEnd = value.ToSKColor();
            InvalidateSurface();
        }
    }

    private SKColor thisInnerBackgroundColorEnd;
    #endregion

    #region Border Color
    public static readonly BindableProperty BorderColorProperty =
        BindableProperty.Create("BorderColor",
            typeof(Color),
            typeof(GradientFrame),
            Color.Blue,
            propertyChanged: (currentControl, oldValue, newValue) =>
            {
                var thisControl = currentControl as GradientFrame;
                thisControl.BorderColor = (Color)newValue;
            });

    public Color BorderColor
    {
        get { return (Color)GetValue(BorderColorProperty); }
        set
        {
            SetValue(BorderColorProperty, value);
            thisBorderColor = value.ToSKColor();
            InvalidateSurface();
        }
    }

    private SKColor thisBorderColor;
    #endregion

    #region Border Width
    public static readonly BindableProperty BorderWidthProperty =
        BindableProperty.Create("BorderWidth",
            typeof(int),
            typeof(GradientFrame),
            1,
            propertyChanged: (currentControl, oldValue, newValue) =>
            {
                var thisControl = currentControl as GradientFrame;
                thisControl.BorderWidth = (int)newValue;
            });

    public int BorderWidth
    {
        get { return (int)GetValue(BorderWidthProperty); }
        set
        {
            SetValue(BorderWidthProperty, value);
            InvalidateSurface();
        }
    }
    #endregion

    #region BorderRadius
    public static readonly BindableProperty BorderRadiusProperty =
        BindableProperty.Create("BorderRadius",
            typeof(float),
            typeof(GradientFrame),
            25f,
            propertyChanged: (currentControl, oldValue, newValue) =>
            {
                var thisControl = currentControl as GradientFrame;
                thisControl.BorderRadius = (float)newValue;
            });

    public float BorderRadius
    {
        get { return (float)GetValue(BorderRadiusProperty); }
        set
        {
            SetValue(BorderRadiusProperty, value);
            InvalidateSurface();
        }
    }
    #endregion

    #region Fill Orientation
    public enum FillOrientations
    {
        Horizontal,
        Vertical
    }

    public static readonly BindableProperty FillOrientationProperty =
        BindableProperty.Create("FillOrientation",
            typeof(FillOrientations),
            typeof(GradientFrame),
            FillOrientations.Horizontal,
            propertyChanged: (currentControl, oldValue, newValue) =>
            {
                var thisControl = currentControl as GradientFrame;
                thisControl.FillOrientation = (FillOrientations)newValue;
            });

    public FillOrientations FillOrientation
    {
        get { return (FillOrientations)GetValue(FillOrientationProperty); }
        set
        {
            SetValue(FillOrientationProperty, value);
            InvalidateSurface();
        }
    }
    #endregion

    public GradientFrame()
    {
    }

    protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
    {
        base.OnPaintSurface(e);

        SKImageInfo info = e.Info;
        SKSurface surface = e.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear(thisOuterBackgroundColor);

        int width = info.Width;
        int height = info.Height;

        SKRect rect = new SKRect
        {
            Left = 1 + BorderWidth,
            Top = 1 + BorderWidth,
            Right = width - 1 - BorderWidth,
            Bottom = height - 1 - BorderWidth
        };

        SKPaint paintBorder = new SKPaint
        {
            Style = SKPaintStyle.Stroke,
            StrokeWidth = 3,
            Color = thisBorderColor,
            IsAntialias = true
        };
        canvas.DrawRoundRect(rect, BorderRadius, BorderRadius, paintBorder);

        var colors = new SKColor[] { thisInnerBackgroundColorStart, thisInnerBackgroundColorEnd };
        SKShader shader = null;
        if (FillOrientation.Equals(FillOrientations.Vertical))
        {
            shader = SKShader.CreateLinearGradient(
                new SKPoint(0, 0),
                new SKPoint(0, 100),
                colors,
                null,
                SKShaderTileMode.Clamp);
        }
        else
        {
            shader = SKShader.CreateLinearGradient(
                new SKPoint(0, 0),
                new SKPoint(100, 0),
                colors,
                null,
                SKShaderTileMode.Clamp);
        }

        var paintFill = new SKPaint()
        { 
            Shader = shader,
            Style = SKPaintStyle.Fill,
            IsAntialias = true
        };

        canvas.DrawRoundRect(rect, BorderRadius, BorderRadius, paintFill);
    }       

}

Posts

  • Viswa_007Viswa_007 Member ✭✭

    SKCanvasView isn't available in .Net standard project, need help please.

    I am looking for similar control.

  • NMackayNMackay GBInsider, University admin

    @Viswa_007 said:
    SKCanvasView isn't available in .Net standard project, need help please.

    I am looking for similar control.

    Yes it is. Ensure you have the nuget package correctly installed in your platform specific projects & you .NETStandard/PCL library. We use SkiaSharp with .NETStandard for gradients.

  • Viswa_007Viswa_007 Member ✭✭

    @NMackay
    It worked, had an issue with nuget pakages.

    Thanks.

  • CatssCatss Member
    edited August 2019

Sign In or Register to comment.