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);
}
}