How about some more Material Design based controls for Xamarin.Forms?
A while back I wrote about creating more “Material” Frames in your Xamarin.Forms apps using a custom renderer for iOS: Xamarin.Tip – Making Your iOS Frame Shadows More Material
And also wrote about the MaterialButton control I created that added dynamic Elevation properties for both iOS and Android:
Xamarin.Tip – Adding Dynamic Elevation to Your Xamarin.Forms Buttons
And I’ve been getting requests to talk about how to do it with the Frame control in Xamarin.Forms. Spoiler Alert: It’s basically the exact same thing as the MaterialButton…..
Let’s start by creating a custom Frame class in our Xamarin.Forms project:
MaterialFrame.cs
public class MaterialFrame : Frame
{
public static BindableProperty ElevationProperty = BindableProperty.Create(nameof(Elevation), typeof(float), typeof(MaterialButton), 4.0f);
public float Elevation
{
get
{
return (float)GetValue(ElevationProperty);
}
set
{
SetValue(ElevationProperty, value);
}
}
}
We add our bindable Elevation property with a default value of 4.
Now we just need simple custom renderers for our iOS and Android implementations.
Starting with iOS:
MaterialFrameRenderer_iOS.cs
public class MaterialFrameRenderer : FrameRenderer
{
public static void Initialize()
{
// empty, but used for beating the linker
}
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
UpdateShadow();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if(e.PropertyName == "Elevation")
{
UpdateShadow();
}
}
private void UpdateShadow()
{
var materialFrame = (MaterialFrame)Element;
// Update shadow to match better material design standards of elevation
Layer.ShadowRadius = materialFrame.Elevation;
Layer.ShadowColor = UIColor.Gray.CGColor;
Layer.ShadowOffset = new CGSize(2, 2);
Layer.ShadowOpacity = 0.80f;
Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath;
Layer.MasksToBounds = false;
}
}
And now the simple Android renderer that can use the built in Elevation properties
MaterialFrameRenderer_Android.cs
public class MaterialFrameRenderer : Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
UpdateElevation();
}
private void UpdateElevation()
{
var materialFrame= (MaterialFrame)Element;
// we need to reset the StateListAnimator to override the setting of Elevation on touch down and release.
Control.StateListAnimator = new Android.Animation.StateListAnimator();
// set the elevation manually
ViewCompat.SetElevation(this, materialFrame.Elevation);
ViewCompat.SetElevation(Control, materialFrame.Elevation);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if(e.PropertyName == "Elevation")
{
UpdateElevation();
}
}
}
Now with both our renderers, we can reference our MaterialFrame component in our content!
<ContentPage ...>
<StackLayout>
<components:MaterialFrame Elevation="6" />
<components:MaterialFrame Elevation="{Binding SomeNumber}"/>
</StackLayout>
</ContentPage>
And that’s it! Now you can control the elevation of your frames for both iOS and Android in Xamarin.Forms:

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!
Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.








