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.