I have a custom frame that I have created in Xamarin.Forms that allows for a gradient background. I am trying to create a compound shape from two different Frames both with a gradient background, but I am wanting the gradient to be shared between the two. I have gotten the desired effect with using Skia.Sharp.Forms but I would like to know if there is a way to do this with just using Xamarin.Forms and custom renderers.
An example of what I am looking for:
An example of what I get when using custom 2 custom frames: (pay no attention to the slightly different shape)
EDIT
My idea is I want to encapsulate the two frames (or any controls for that matter) in a Custom grid that is given the gradient colors. Then in the custom renderer of the Grid it sets the backgrounds of the children controls to the gradient. This way the LinearGradient
has the starting point (0,0) of the parent grid and isn't creating a new gradient for each child. Here's some code to explain what I mean, I just havent figured out the part where I set the children's backgrounds to the gradient yet, the SetLayerPaint
( method doesnt seem to work..)
protected override void DispatchDraw(Canvas canvas)
{
_gradient = new Android.Graphics.LinearGradient(
0, 0, Width, Height,
new int[] { _startColor.ToAndroid(), _middleColor.ToAndroid(), _endColor.ToAndroid() },
null,
Android.Graphics.Shader.TileMode.Mirror);
for(var i = 0; i < ChildCount; i++ )
{
var paint = new Android.Graphics.Paint()
{
Dither = true
};
paint.SetShader(_gradient);
var child = GetChildAt(i);
child.SetLayerPaint(paint);
}
base.DispatchDraw(canvas);
}
Does anyone know if this is possible?
Here is my solution: The custom renderer for the Grid
public class GradientGridRenderer_Android : ViewRenderer
{
private Xamarin.Forms.Color _startColor;
private Xamarin.Forms.Color _middleColor;
private Xamarin.Forms.Color _endColor;
LinearGradient _gradient;
public GradientGridRenderer_Android(Context context)
: base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if(e.NewElement != null && e.NewElement is GradientGrid grid)
{
_startColor = grid.StartColor;
_middleColor = grid.MiddleColor;
_endColor = grid.EndColor;
}
}
protected override void DispatchDraw(Canvas canvas)
{
base.DispatchDraw(canvas);
_gradient = new LinearGradient(
0, 0, Width, Height,
new int[]
{
_startColor.ToAndroid(),
_middleColor.ToAndroid(),
_endColor.ToAndroid(),
},
null,
Shader.TileMode.Mirror);
for (var i = 0; i < ChildCount; i++)
{
var child = GetChildAt(i);
if(child is FrameRenderer_Android gFrame)
{
gFrame.Gradient = _gradient;
gFrame.Invalidate();
}
}
}
}
Here is the custom renderer for the Child, if needed you could abstract this out and make any number of custom renderer's that take a gradient, but for my purposes I just needed a Frame.
public class FrameRenderer_Android : Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer
{
public LinearGradient Gradient;
public FrameRenderer_Android(Context context)
: base(context) { }
protected override void DispatchDraw(Canvas canvas)
{
if(Control != null && Gradient != null)
{
var paint = new Android.Graphics.Paint()
{
Dither = true,
};
paint.SetShader(Gradient);
canvas.DrawPaint(paint);
}
base.DispatchDraw(canvas);
}
}
And here is the xaml
<ContentPage.Content>
<cntrl:GradientGrid RowSpacing="0"
Margin="0,20,0,0"
StartColor="{StaticResource GracePink}"
MiddleColor="{StaticResource GracePurple}"
EndColor="{StaticResource GraceDarkPurple}"
IsClippedToBounds="True">
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="90*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<cntrl:CustomFrame Grid.Column="2"
Grid.Row="0"
IsClippedToBounds="True"
CornerRadius="20,20,0,0">
</cntrl:CustomFrame>
<cntrl:CustomFrame Grid.ColumnSpan="4"
Grid.Row="1"
IsClippedToBounds="True"
CornerRadius="40,40,0,0">
</cntrl:CustomFrame>
</cntrl:GradientGrid>
</ContentPage.Content>