frame border width in Xamarin.Forms

I tried to dance with FrameRenderer for Android and I found some solution. Bad side is, that to be border color, width and corner radius variables visible inside MyFrameRenderer class, I had to create MyFrame:Frame class to affect only my own frame type. Close enough for my purpose... So:

namespace PROJECT
{
    public class MyFrame : Xamarin.Forms.Frame
    {
        public static float myFrameWidth = 2;
        public static float myCornerRadius = 12;
        public static Color myFrameColor = Color.Red;
        public static Color myBackgroundColor = Color.Black;

        public MyFrame() { }
    }
}

...

[assembly: ExportRenderer(typeof(PROJECT.MyFrame), typeof(PROJECT.Droid.MyFrameRenderer))]
namespace PROJECT.Droid
{
    class MyFrameRenderer : FrameRenderer
    {
        protected override void OnDraw(Android.Graphics.Canvas canvas)
        {
            // canvas contains image of standard outline
            // to "hide" it, not efficent but sometimes "close enough solution"
            // is to overlay that outline by new one in our's page background color
            // then draw a new one in prefered style
            // or... just draw thicker one over the old

            var my1stPaint = new Android.Graphics.Paint();
            var my2ndPaint = new Android.Graphics.Paint();
            var backgroundPaint = new Android.Graphics.Paint();

            my1stPaint.AntiAlias = true;
            my1stPaint.SetStyle(Paint.Style.Stroke);
            my1stPaint.StrokeWidth = MyFrame.myFrameWidth + 2;
            my1stPaint.Color = MyFrame.myFrameColor.ToAndroid();

            my2ndPaint.AntiAlias = true;
            my2ndPaint.SetStyle(Paint.Style.Stroke);
            my2ndPaint.StrokeWidth = MyFrame.myFrameWidth;
            my2ndPaint.Color = MyFrame.myBackgroundColor.ToAndroid();

            backgroundPaint.SetStyle(Paint.Style.Stroke);
            backgroundPaint.StrokeWidth = 4;
            backgroundPaint.Color = MyFrame.myBackgroundColor.ToAndroid();

            Rect oldBounds = new Rect();
            canvas.GetClipBounds(oldBounds);

            RectF oldOutlineBounds = new RectF();
            oldOutlineBounds.Set(oldBounds);

            RectF myOutlineBounds = new RectF();
            myOutlineBounds.Set(oldBounds);
            myOutlineBounds.Top += (int)my2ndPaint.StrokeWidth+3;
            myOutlineBounds.Bottom -= (int)my2ndPaint.StrokeWidth+3;
            myOutlineBounds.Left += (int)my2ndPaint.StrokeWidth+3;
            myOutlineBounds.Right -= (int)my2ndPaint.StrokeWidth+3;

            canvas.DrawRoundRect(oldOutlineBounds, 10, 10, backgroundPaint); //to "hide" old outline
            canvas.DrawRoundRect(myOutlineBounds, MyFrame.myCornerRadius, MyFrame.myCornerRadius, my1stPaint);
            canvas.DrawRoundRect(myOutlineBounds, MyFrame.myCornerRadius, MyFrame.myCornerRadius, my2ndPaint);

            base.OnDraw(canvas);
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Frame> e)
        {
            base.OnElementChanged(e);
        }
    }
}

enter image description here


Just wrap your frame into another frame giving it background color that you want you bordercolor to be. And give the wrapper-frame padding. Here is an example of the round image with border.

<Frame CornerRadius="60" HeightRequest="100" WidthRequest="100" BackgroundColor="White" HorizontalOptions="Center" VerticalOptions="Start" Padding="2" IsClippedToBounds="True">
    <Frame CornerRadius="60" HeightRequest="100" WidthRequest="100" BackgroundColor="White" HorizontalOptions="Center" VerticalOptions="Start" Margin="0" Padding="0" IsClippedToBounds="True">
            <Image Source="dp.jpg" HeightRequest="40" WidthRequest="40" Aspect="AspectFill"></Image>
    </Frame>
</Frame>

You can either create your own implementation with effects or extend the FreshEssentials open source library. They have a control called AdvancedFrame that provides custom renderers for the Frame control on all platforms.

If you look at each platform specific project, you'll notice the AdvancedFrameRenderer classes that create bezier paths for rounded corner support. You'll just need to dive into the Draw method on each platform (iOS, Android) and figure out how to set the stroke width.

It's easiest to start from Android since there the stroke width is defined in the code already (on this line). You'll just want to create a property for that in the AdvancedFrame control so you can have a different width on each control. I'm not sure how to set the stroke width on iOS but it's using UIBezierPath which should be rather easy to modify.


You can try something like this :

<Frame HasShadow="False" CornerRadius="25" Padding="2" BackgroundColor="#F69927">
    <Frame HasShadow="False" CornerRadius="23" BackgroundColor="White" Padding="12">
        <StackLayout Orientation="Horizontal" HorizontalOptions="CenterAndExpand" VerticalOptions="Start">
            <!-- Content -->                
        </StackLayout>
    </Frame>
</Frame>