Xamarin.Tip – Borderless Inputs

I published multiple posts this week about creating Xamarin.Forms controls without borders using Custom renderers. This post is your one stop shop for all these posts. These are the controls that are used in my repository to create Material Design inputs in Xamarin.Forms that you can find here:
https://github.com/SuavePirate/SuaveControls.MaterialFormControls. These will be talked about in posts to come!
Check the borderless controls out here:

  1. Xamarin.Forms Borderless Entry
  2. Xamarin.Forms Borderless Picker
  3. Xamarin.Forms Borderless DatePicker
  4. Xamarin.Forms Borderless TimePicker
  5. Xamarin.Forms Borderless Editor

And check out how they look here:

BorderlessEntry


BorderlessEditor

BorderlessPicker

BorderlessDatePicker

BorderlessTimePicker

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.

Xamarin.Tip – Borderless Editor

I previously put out a post on removing the border of a Xamarin.Forms Entry which was then used to create a custom PinView as well as a MaterialEntry that follows the material design standards for text fields. Check those out here:

In this post, we’ll apply some of the same principles to create a BorderlessEditor. It’s going to use a simple custom renderer, although this could and should be done using an Effect if being used on its own. However, this BorderlessEditor will be the foundation for future controls.

You can find this code as part of my library in progress to create Material Design Form controls for Xamarin.Forms – https://github.com/SuavePirate/SuaveControls.MaterialFormControls.

Let’s get started with our custom control by first creating a custom subclass of Xamarin.Forms.Editor followed by a custom renderer class for iOS, Android, and UWP that kills the border.

BorderlessEditor.cs

namespace SuaveControls.MaterialForms
{
    public class BorderlessEditor : Editor
    {
    }
}

Nothing special here since we are using the default behavior of the Editor.

Android

Now let’s create an Android custom renderer.

BorderlessEditorRenderer.cs – Android

[assembly: ExportRenderer(typeof(BorderlessEditor), typeof(BorderlessEditorRenderer))]
namespace SuaveControls.MaterialForms.Android.Renderers
{
    public class BorderlessEditorRenderer : EditorRenderer
    {
        public static void Init() { }
        protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement == null)
            {
                Control.Background = null;

                var layoutParams = new MarginLayoutParams(Control.LayoutParameters);
                layoutParams.SetMargins(0, 0, 0, 0);
                LayoutParameters = layoutParams;
                Control.LayoutParameters = layoutParams;
                Control.SetPadding(0, 0, 0, 0);
                SetPadding(0, 0, 0, 0);
            }
        }
    }
}

We simple kill the default padding and margins while setting the Background property to null. This Background is what creates the drawable underline for the AppCompat Editor.

iOS

Follow with an iOS renderer.

BorderlessEditorRenderer.cs – iOS

[assembly: ExportRenderer(typeof(BorderlessEditor), typeof(BorderlessEditorRenderer))]
namespace SuaveControls.MaterialForms.iOS.Renderers
{
    public class BorderlessEditorRenderer : EditorRenderer
    {
        public static void Init() { }
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            Control.Layer.BorderWidth = 0;
        }
    }
}

All we do here is set the BorderWidth to 0.

UWP

Lastly a renderer for UWP

BorderlessEditorRenderer.cs – UWP


[assembly: ExportRenderer(typeof(BorderlessEditor), typeof(BorderlessEditorRenderer))]

namespace SuaveControls.MaterialForms.UWP.Renderers
{
    public class BorderlessEditorRenderer : EditorRenderer
    {
        public static void Init() { }
        protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                Control.BorderThickness = new Windows.UI.Xaml.Thickness(0);
                Control.Margin = new Windows.UI.Xaml.Thickness(0);
                Control.Padding = new Windows.UI.Xaml.Thickness(0);
            }
        }
    }
}

Similar to how we did it on Android, we set both the Margin and Padding to 0 and also set the BorderThickness to a 0’d Thickness.

Using the BorderlessEditor

Now you can use the BorderlessEditor in your XAML or C# code:

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ExampleMaterialApp"
             xmlns:suave="clr-namespace:SuaveControls.MaterialForms;assembly=SuaveControls.MaterialForms"
             x:Class="ExampleMaterialApp.MainPage">

    <ScrollView>
        <StackLayout Spacing="16" Margin="16" BackgroundColor="Blue">
            <Label Text="Borderless Editor!" Margin="32" HorizontalOptions="Center" HorizontalTextAlignment="Center"/>
            <suave:BorderlessEditor BackgroundColor="Black" TextColor="White" HeightRequest="300" Margin="32"/>

        </StackLayout>
    </ScrollView>

</ContentPage>

Check out those results on iOS:

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.

Xamarin.Tip – Borderless TimePicker

I previously put out a post on removing the border of a Xamarin.Forms Entry which was then used to create a custom PinView as well as a MaterialEntry that follows the material design standards for text fields. I also added a post just like this one that talks about creating a BorderlessPicker. Check those out here:

In this post, we’ll do exactly what we did with the BorderlessPicker, but apply it to the Xamarin.Forms.TimePicker control to remove the border. This would ideally be done using an Effect, however we will be using this control in a later post to create a MaterialTimePicker to fit the Material Design standards for form inputs, so we will create custom renderers for Android, iOS, and UWP.

You can find this code as part of my library in progress to create Material Design Form controls for Xamarin.Forms – https://github.com/SuavePirate/SuaveControls.MaterialFormControls.

Let’s get started with our custom control by first creating a custom subclass of Xamarin.Forms.TimePicker followed by a custom renderer class for iOS, Android, and UWP that kills the border.

BorderlessTimePicker.cs

namespace SuaveControls.MaterialForms
{
    public class BorderlessTimePicker : TimePicker
    {
    }
}

Nothing special here since we are using the default behavior of the TimePicker.

Android

Now let’s create an Android custom renderer.

BorderlessTimePickerRenderer.cs – Android

[assembly: ExportRenderer(typeof(BorderlessTimePicker), typeof(BorderlessTimePickerRenderer))]
namespace SuaveControls.MaterialForms.Android.Renderers
{
    public class BorderlessTimePickerRenderer : TimePickerRenderer
    {
        public static void Init() { }
        protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement == null)
            {
                Control.Background = null;

                var layoutParams = new MarginLayoutParams(Control.LayoutParameters);
                layoutParams.SetMargins(0, 0, 0, 0);
                LayoutParameters = layoutParams;
                Control.LayoutParameters = layoutParams;
                Control.SetPadding(0, 0, 0, 0);
                SetPadding(0, 0, 0, 0);
            }
        }
    }
}

We simple kill the default padding and margins while setting the Background property to null. This Background is what creates the drawable underline for the AppCompat TimePicker.

iOS

Follow with an iOS renderer.

BorderlessTimePickerRenderer.cs – iOS

[assembly: ExportRenderer(typeof(BorderlessTimePicker), typeof(BorderlessTimePickerRenderer))]
namespace SuaveControls.MaterialForms.iOS.Renderers
{
    public class BorderlessTimePickerRenderer : TimePickerRenderer
    {
        public static void Init() { }
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            Control.Layer.BorderWidth = 0;
            Control.BorderStyle = UITextBorderStyle.None;
        }
    }
}

All we do here is set the BorderWidth to 0 and the BorderStyle to UITextBorderStyle.None.

UWP

Lastly a renderer for UWP

BorderlessTimePickerRenderer.cs – UWP


[assembly: ExportRenderer(typeof(BorderlessTimePicker), typeof(BorderlessTimePickerRenderer))]

namespace SuaveControls.MaterialForms.UWP.Renderers
{
    public class BorderlessTimePickerRenderer : TimePickerRenderer
    {
        public static void Init() { }
        protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                Control.BorderThickness = new Windows.UI.Xaml.Thickness(0);
                Control.Margin = new Windows.UI.Xaml.Thickness(0);
                Control.Padding = new Windows.UI.Xaml.Thickness(0);
            }
        }
    }
}

Similar to how we did it on Android, we set both the Margin and Padding to 0 and also set the BorderThickness to a 0’d Thickness.

Using the BorderlessTimePicker

Now you can use the BorderlessTimePicker in your XAML or C# code:

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ExampleMaterialApp"
             xmlns:suave="clr-namespace:SuaveControls.MaterialForms;assembly=SuaveControls.MaterialForms"
             x:Class="ExampleMaterialApp.MainPage">

    <ScrollView>
        <StackLayout Spacing="16" Margin="16">
            <Label Text="Borderless TimePicker!" Margin="32" HorizontalOptions="Center" HorizontalTextAlignment="Center"/>
            <suave:BorderlessTimePicker/>

        </StackLayout>
    </ScrollView>

</ContentPage>

Check out those results on iOS:

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.

Xamarin.Tip – Borderless DatePicker

I previously put out a post on removing the border of a Xamarin.Forms Entry which was then used to create a custom PinView as well as a MaterialEntry that follows the material design standards for text fields. I also added a post just like this one that talks about creating a BorderlessPicker. Check those out here:

In this post, we’ll do exactly what we did with the BorderlessPicker, but apply it to the Xamarin.Forms.DatePicker control to remove the border. This would ideally be done using an Effect, however we will be using this control in a later post to create a MaterialDatePicker to fit the Material Design standards for form inputs, so we will create custom renderers for Android, iOS, and UWP.

You can find this code as part of my library in progress to create Material Design Form controls for Xamarin.Forms – https://github.com/SuavePirate/SuaveControls.MaterialFormControls.

Let’s get started with our custom control by first creating a custom subclass of Xamarin.Forms.DatePicker followed by a custom renderer class for iOS, Android, and UWP that kills the border.

BorderlessDatePicker.cs

namespace SuaveControls.MaterialForms
{
    public class BorderlessDatePicker : DatePicker
    {
    }
}

Nothing special here since we are using the default behavior of the DatePicker.

Android

Now let’s create an Android custom renderer.

BorderlessDatePickerRenderer.cs – Android

[assembly: ExportRenderer(typeof(BorderlessDatePicker), typeof(BorderlessDatePickerRenderer))]
namespace SuaveControls.MaterialForms.Android.Renderers
{
    public class BorderlessDatePickerRenderer : DatePickerRenderer
    {
        public static void Init() { }
        protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement == null)
            {
                Control.Background = null;

                var layoutParams = new MarginLayoutParams(Control.LayoutParameters);
                layoutParams.SetMargins(0, 0, 0, 0);
                LayoutParameters = layoutParams;
                Control.LayoutParameters = layoutParams;
                Control.SetPadding(0, 0, 0, 0);
                SetPadding(0, 0, 0, 0);
            }
        }
    }
}

We simple kill the default padding and margins while setting the Background property to null. This Background is what creates the drawable underline for the AppCompat DatePicker.

iOS

Follow with an iOS renderer.

BorderlessDatePickerRenderer.cs – iOS

[assembly: ExportRenderer(typeof(BorderlessDatePicker), typeof(BorderlessDatePickerRenderer))]
namespace SuaveControls.MaterialForms.iOS.Renderers
{
    public class BorderlessDatePickerRenderer : DatePickerRenderer
    {
        public static void Init() { }
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            Control.Layer.BorderWidth = 0;
            Control.BorderStyle = UITextBorderStyle.None;
        }
    }
}

All we do here is set the BorderWidth to 0 and the BorderStyle to UITextBorderStyle.None.

UWP

Lastly a renderer for UWP

BorderlessDatePickerRenderer.cs – UWP


[assembly: ExportRenderer(typeof(BorderlessDatePicker), typeof(BorderlessDatePickerRenderer))]

namespace SuaveControls.MaterialForms.UWP.Renderers
{
    public class BorderlessDatePickerRenderer : DatePickerRenderer
    {
        public static void Init() { }
        protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                Control.BorderThickness = new Windows.UI.Xaml.Thickness(0);
                Control.Margin = new Windows.UI.Xaml.Thickness(0);
                Control.Padding = new Windows.UI.Xaml.Thickness(0);
            }
        }
    }
}

Similar to how we did it on Android, we set both the Margin and Padding to 0 and also set the BorderThickness to a 0’d Thickness.

Using the BorderlessDatePicker

Now you can use the BorderlessDatePicker in your XAML or C# code:

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ExampleMaterialApp"
             xmlns:suave="clr-namespace:SuaveControls.MaterialForms;assembly=SuaveControls.MaterialForms"
             x:Class="ExampleMaterialApp.MainPage">

    <ScrollView>
        <StackLayout Spacing="16" Margin="16">
            <Label Text="Borderless DatePicker!" Margin="32" HorizontalOptions="Center" HorizontalTextAlignment="Center"/>
            <suave:BorderlessDatePicker/>

        </StackLayout>
    </ScrollView>

</ContentPage>

Check out those results on iOS:

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.

Xamarin.Tip – Borderless Picker

I previously put out a post on removing the border of a Xamarin.Forms Entry which was then used to create a custom PinView as well as a MaterialEntry that follows the material design standards for text fields. Check those out here:

In this post, we’ll apply some of the same principles to create a BorderlessPicker. It’s going to use a simple custom renderer, although this could and should be done using an Effect if being used on its own. However, this BorderlessPicker will be the foundation for future controls we will create such as the MaterialPicker that applies Material Design standards to the Xamarin.Forms Picker control on Android, iOS, and UWP.

You can find this code as part of my library in progress to create Material Design Form controls for Xamarin.Forms – https://github.com/SuavePirate/SuaveControls.MaterialFormControls.

Let’s get started with our custom control by first creating a custom subclass of Xamarin.Forms.Picker followed by a custom renderer class for iOS, Android, and UWP that kills the border.

BorderlessPicker.cs

namespace SuaveControls.MaterialForms
{
    public class BorderlessPicker : Picker
    {
    }
}

Nothing special here since we are using the default behavior of the Picker.

Android

Now let’s create an Android custom renderer.

BorderlessPickerRenderer.cs – Android

[assembly: ExportRenderer(typeof(BorderlessPicker), typeof(BorderlessPickerRenderer))]
namespace SuaveControls.MaterialForms.Android.Renderers
{
    public class BorderlessPickerRenderer : PickerRenderer
    {
        public static void Init() { }
        protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement == null)
            {
                Control.Background = null;

                var layoutParams = new MarginLayoutParams(Control.LayoutParameters);
                layoutParams.SetMargins(0, 0, 0, 0);
                LayoutParameters = layoutParams;
                Control.LayoutParameters = layoutParams;
                Control.SetPadding(0, 0, 0, 0);
                SetPadding(0, 0, 0, 0);
            }
        }
    }
}

We simple kill the default padding and margins while setting the Background property to null. This Background is what creates the drawable underline for the AppCompat Picker.

iOS

Follow with an iOS renderer.

BorderlessPickerRenderer.cs – iOS

[assembly: ExportRenderer(typeof(BorderlessPicker), typeof(BorderlessPickerRenderer))]
namespace SuaveControls.MaterialForms.iOS.Renderers
{
    public class BorderlessPickerRenderer : PickerRenderer
    {
        public static void Init() { }
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            Control.Layer.BorderWidth = 0;
            Control.BorderStyle = UITextBorderStyle.None;
        }
    }
}

All we do here is set the BorderWidth to 0 and the BorderStyle to UITextBorderStyle.None.

UWP

Lastly a renderer for UWP

BorderlessPickerRenderer.cs – UWP


[assembly: ExportRenderer(typeof(BorderlessPicker), typeof(BorderlessPickerRenderer))]

namespace SuaveControls.MaterialForms.UWP.Renderers
{
    public class BorderlessPickerRenderer : PickerRenderer
    {
        public static void Init() { }
        protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                Control.BorderThickness = new Windows.UI.Xaml.Thickness(0);
                Control.Margin = new Windows.UI.Xaml.Thickness(0);
                Control.Padding = new Windows.UI.Xaml.Thickness(0);
            }
        }
    }
}

Similar to how we did it on Android, we set both the Margin and Padding to 0 and also set the BorderThickness to a 0’d Thickness.

Using the BorderlessPicker

Now you can use the BorderlessPicker in your XAML or C# code:

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ExampleMaterialApp"
             xmlns:suave="clr-namespace:SuaveControls.MaterialForms;assembly=SuaveControls.MaterialForms"
             x:Class="ExampleMaterialApp.MainPage">

    <ScrollView>
        <StackLayout Spacing="16" Margin="16">
            <Label Text="Borderless Picker!" Margin="32" HorizontalOptions="Center" HorizontalTextAlignment="Center"/>
            <suave:BorderlessPicker x:Name="PickerDemo" Title="Options"/>

        </StackLayout>
    </ScrollView>

</ContentPage>

MainPage.xaml.cs

    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            PickerDemo.ItemsSource = new List<string>
            {
                "Option 1",
                "Option 2",
                "Option 3",
                "Option 4"
            };
        }
    }

Check out those results on iOS:

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.

Xamarin.Control – Xamarin.Forms MaterialEntry

Back by popular demand, bringing more Material Design controls to you Xamarin.Forms app! This time we will look at implementing the standards in Material Design’s text fields by building a MaterialEntry control. You can find the source code and example app here: https://github.com/SuavePirate/MaterialEntry but you can build your own by following this post.

Let’s build our Xamarin.Forms control to work the same on not only Android where Material Design is baked in, but also to run on iOS AND UWP. In the end, we should be able to use our floating label, set an accent color that expands on the label and underline when focused while being able to bind these properties through MVVM.

Simulator Screen Shot Jul 14, 2017, 3.42.03 PM

The first thing we need to do is create a BorderlessEntry that removes the border from our entry on all 3 platforms. I’ve done this in a previous blog post here: Xamarin.Forms Borderless Entry, so we won’t be implementing it here. This code is also in the GitHub link above.

With our BorderlessEntry we can now create our custom control WITHOUT ANY MORE CUSTOM RENDERERS!

Let’s set up the layout structure in our XAML file, and then wire up the animation logic in our code behind.

MaterialEntry.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"               xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:local="clr-namespace:SuaveControls.MaterialEntry"              x:Class="SuaveControls.MaterialEntry.MaterialEntry">
  <ContentView.Content>
        <Grid ColumnSpacing="16" Margin="0,8">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="1"/>
            </Grid.RowDefinitions>
            <Label x:Name="HiddenLabel" FontSize="10" IsVisible="False" Margin="0"/>
            <local:BorderlessEntry x:Name="EntryField" Text="{Binding Text, Mode=TwoWay}" Margin="0,12,0,0"/>
            <BoxView x:Name="BottomBorder" BackgroundColor="Gray"  Grid.Row="1" HeightRequest="1" Margin="0" HorizontalOptions="FillAndExpand"/>
            <BoxView x:Name="HiddenBottomBorder" BackgroundColor="Gray" Grid.Row="1" HeightRequest="1" Margin="0" WidthRequest="0" HorizontalOptions="Center"/>
        </Grid>
    </ContentView.Content>
</ContentView>

We set up our BorderlessEntry that will act as our formal point for entering text. We also add a label that is initially hidden and laid out on top of the BorderlessEntry. This is the label we will be using to animate the floating action that Material Design uses based while we fade out the placeholder text. The last bit is two BoxViews that act as the bottom line below the Entry. One is the unfocused which has a standard gray color, while the other has a width of 0 and will have a background color of our selected AccentColor. This will have an animated width expansion when the BorderlessEntry is focused.

Now let’s look at the animation and bindings in the code behind:

MaterialEntry.xaml.cs

public partial class MaterialEntry : ContentView
    {
        public static void Init() { }
        public static BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(MaterialEntry), defaultBindingMode: BindingMode.TwoWay);
        public static BindableProperty PlaceholderProperty = BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(MaterialEntry), defaultBindingMode: BindingMode.TwoWay, propertyChanged: (bindable, oldVal, newval) =>
        {
            var matEntry = (MaterialEntry)bindable;
            matEntry.EntryField.Placeholder = (string)newval;
            matEntry.HiddenLabel.Text = (string)newval;
        });

        public static BindableProperty IsPasswordProperty = BindableProperty.Create(nameof(IsPassword), typeof(bool), typeof(MaterialEntry), defaultValue: false, propertyChanged: (bindable, oldVal, newVal) =>
        {
            var matEntry = (MaterialEntry)bindable;
            matEntry.EntryField.IsPassword = (bool)newVal;
        });
        public static BindableProperty KeyboardProperty = BindableProperty.Create(nameof(Keyboard), typeof(Keyboard), typeof(MaterialEntry), defaultValue: Keyboard.Default, propertyChanged: (bindable, oldVal, newVal) =>
        {
            var matEntry = (MaterialEntry)bindable;
            matEntry.EntryField.Keyboard = (Keyboard)newVal;
        });
        public static BindableProperty AccentColorProperty = BindableProperty.Create(nameof(AccentColor), typeof(Color), typeof(MaterialEntry), defaultValue: Color.Accent);
        public Color AccentColor
        {
            get
            {
                return (Color)GetValue(AccentColorProperty);
            }
            set
            {
                SetValue(AccentColorProperty, value);
            }
        }
        public Keyboard Keyboard
        {
            get
            {
                return (Keyboard)GetValue(KeyboardProperty);
            }
            set
            {
                SetValue(KeyboardProperty, value);
            }
        }

        public bool IsPassword
        {
            get
            {
                return (bool)GetValue(IsPasswordProperty);
            }
            set
            {
                SetValue(IsPasswordProperty, value);
            }
        }

        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }
        public string Placeholder
        {
            get
            {
                return (string)GetValue(PlaceholderProperty);
            }
            set
            {
                SetValue(PlaceholderProperty, value);
            }
        }
        public MaterialEntry()
        {
            InitializeComponent();
            EntryField.BindingContext = this;
            EntryField.Focused += async (s, a) =>
            {
                HiddenBottomBorder.BackgroundColor = AccentColor;
                HiddenLabel.TextColor = AccentColor;
                HiddenLabel.IsVisible = true;
                if (string.IsNullOrEmpty(EntryField.Text))
                {
                    // animate both at the same time
                    await Task.WhenAll(
                        HiddenBottomBorder.LayoutTo(new Rectangle(BottomBorder.X, BottomBorder.Y, BottomBorder.Width, BottomBorder.Height), 200),
                        HiddenLabel.FadeTo(1, 60),
                        HiddenLabel.TranslateTo(HiddenLabel.TranslationX, EntryField.Y - EntryField.Height + 4, 200, Easing.BounceIn)
                     );
                    EntryField.Placeholder = null;
                }
                else
                {
                    await HiddenBottomBorder.LayoutTo(new Rectangle(BottomBorder.X, BottomBorder.Y, BottomBorder.Width, BottomBorder.Height), 200);
                }
            };
            EntryField.Unfocused += async (s, a) =>
            {
                HiddenLabel.TextColor = Color.Gray;
                if (string.IsNullOrEmpty(EntryField.Text))
                {
                    // animate both at the same time
                    await Task.WhenAll(
                        HiddenBottomBorder.LayoutTo(new Rectangle(BottomBorder.X, BottomBorder.Y, 0, BottomBorder.Height), 200),
                        HiddenLabel.FadeTo(0, 180),
                        HiddenLabel.TranslateTo(HiddenLabel.TranslationX, EntryField.Y, 200, Easing.BounceIn)
                     );
                    EntryField.Placeholder = Placeholder;
                }
                else
                {
                    await HiddenBottomBorder.LayoutTo(new Rectangle(BottomBorder.X, BottomBorder.Y, 0, BottomBorder.Height), 200);
                }
            };
        }
    }

We first set up the BindableProperties and public properties to enable the binding of the AccentColor, Text, Placeholder, and Keyboard. These BindableProperties also handle their own PropertyChanged events to update the view elements dynamically.

After that, we handle our constructor and wire up our Focused and Unfocused events on our BorderlessEntry. In the Focused event, we set the colors of the hidden bar, and the floating label to the accent color. We then start the animations of expanding the hidden bar, and the fade in and float up of the floating label.

On the Unfocused event, we do the inverse of setting the floating label color back to the unfocused color, check if there is text, if there is not – float the label back down, and then animate the collapse of the colored bar.

With all these things together, we get a nicely animated text field that has a floating label and expanding bottom bar with a given accent color!

ios_Material_Entry2

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.

Xamarin.Tip – PCL Profile Problems

New to Xamarin now that Microsoft is pushing it harder with Visual Studio 2017 and Visual Studio for Mac? Can’t create a new PCL and reference it in your Out of the Box Xamarin.Forms project? This post is for you, and I assure you – you are not alone.

 

The Problem

The core of the problem is this shift within the Microsoft Stack from many different versions of the .NET framework and tools into a “standard” – .NET Standard. However, certain parts of Xamarin aren’t quite there. These are things that worked in Visual Studio 2015, but are now new problems for VS 2017 and VS for Mac, especially if you’re working on a team that uses a mix of these.

There are multiple types of Profiles available for a Portable Class Library. Here’s a quick matrix of the different Profiles and where they compare to .NET Standard versions and supported profiles:

PCL Profile .NET Standard PCL Platforms
Profile7 1.1 .NET Framework 4.5, Windows 8
Profile31 1.0 Windows 8.1, Windows Phone Silverlight 8.1
Profile32 1.2 Windows 8.1, Windows Phone 8.1
Profile44 1.2 .NET Framework 4.5.1, Windows 8.1
Profile49 1.0 .NET Framework 4.5, Windows Phone Silverlight 8
Profile78 1.0 .NET Framework 4.5, Windows 8, Windows Phone Silverlight 8
Profile84 1.0 Windows Phone 8.1, Windows Phone Silverlight 8.1
Profile111 1.1 .NET Framework 4.5, Windows 8, Windows Phone 8.1
Profile151 1.2 .NET Framework 4.5.1, Windows 8.1, Windows Phone 8.1
Profile157 1.0 Windows 8.1, Windows Phone 8.1, Windows Phone Silverlight 8.1
Profile259 1.0 .NET Framework 4.5, Windows 8, Windows Phone 8.1, Windows Phone Silverlight 8

Now we can look at the different .NET Standard versions and their supported platforms:

 

.NET Standard 1.0 1.1 1.2 1.3 1.4 1.5 1.6 2.0
.NET Core 1.0 1.0 1.0 1.0 1.0 1.0 1.0 2.0
.NET Framework (with tooling 1.0) 4.5 4.5 4.5.1 4.6 4.6.1 4.6.2
.NET Framework (with tooling 2.0 preview) 4.5 4.5 4.5.1 4.6 4.6.1 4.6.1 4.6.1 4.6.1
Mono 4.6 4.6 4.6 4.6 4.6 4.6 4.6 vNext
Xamarin.iOS 10.0 10.0 10.0 10.0 10.0 10.0 10.0 vNext
Xamarin.Android 7.0 7.0 7.0 7.0 7.0 7.0 7.0 vNext
Universal Windows Platform 10.0 10.0 10.0 10.0 10.0 vNext vNext vNext
Windows 8.0 8.0 8.1
Windows Phone 8.1 8.1 8.1
Windows Phone Silverlight 8.0

 

Xamarin.Forms is created to support everything from Windows Silverlight 8.0 and up. This means that Xamarin.Forms projects are created using a Profile 259 PCL. This is fine, however, Profile 259 is on it’s way out the door, and therefore can’t be created in Visual Studio 2017! When you create a new PCL in VS 2017, it is created with the latest version of .NET Standard that supports .NET Core, UWP, and Xamarin (as of now this is default to Profile7. However, this is incompatible with Profile 259 and therefore cannot be referenced by your newly created Xamarin.Forms project. There’s no way in the IDE or properties to get it to 259, so if you do still want to support Windows Phone and Windows 8 with your Xamarin Projects in Visual Studio 2017, read below. If you don’t, update your Xamarin.Forms project to remove support for Windows 8 and Windows phone and your profiles will be matching again.

Here’s what the default Xamarin.Forms project targets are:
Screen Shot 2017-07-03 at 11.42.45 AM

And here is what a newly created PCL project targets are with everything selected:

Screen Shot 2017-07-03 at 11.42.58 AM

 

The Solution

We need to get our newly created PCLs to Profile 259 in order to reference them in our Xamarin.Forms project. We can’t do this in the IDE, so we need to dive into the .csproj file itself and make some changes:

NewPCL.csproj

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import ... />
  <PropertyGroup>
    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{4a6a9117-1715-47ea-a6c7-6b0fd5b31bdb}</ProjectGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>NewPCL</RootNamespace>
    <AssemblyName>NewPCL</AssemblyName>
    <DefaultLanguage>en-US</DefaultLanguage>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

    <!-- THIS IS WHAT NEEDS TO CHANGE -->
    <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>


    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
  </PropertyGroup>
...
</Project>

Take that line of Profile7 and update it to Profile259 so that it looks like this all together:

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import ... />
  <PropertyGroup>
    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{4a6a9117-1715-47ea-a6c7-6b0fd5b31bdb}</ProjectGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>NewPCL</RootNamespace>
    <AssemblyName>NewPCL</AssemblyName>
    <DefaultLanguage>en-US</DefaultLanguage>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

    <!-- THIS IS WHAT HAS CHANGED -->
    <TargetFrameworkProfile>Profile259</TargetFrameworkProfile>


    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
  </PropertyGroup>
...
</Project>

 
Now you’ll be able to add this project as a reference in your Xamarin.Forms PCL project and continue on.

I know this is pretty annoying to do on large projects that contain many different project files. Take a look at my Onion Architecture project as an example…

This is something that is happening because of this weird in-between state we are at with .NET tooling and the move to .NET Standard. Going forward, Xamarin.Forms will be dropping support for Windows Phone Silverlight which will no longer require Profile259 in order to build our applications in Xamarin.Forms. However, for now, this is the fix for new projects!

Still Broken?

Still having issues with your project structures? Leave a comment and I’ll try to help resolve it.

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.

Xamarin.Tip – Fixing the VS for Mac Xamarin.Forms Template Android Issues

Here’s another quick resolution to one of the most annoying issues I’ve faced in working with Visual Studio for Mac…

The Problem

When creating a brand new, out of the box, Xamarin.Forms project, it didn’t work. This has been a common trend for Xamarin/Microsoft over the last year or so as they roll out Xamarin + Visual Studio updates. The latest culprit was that the Android project was never able to actually install the Xamarin.Forms package from nuget. This was not obvious during the actual build, since the project DID load successfully, and the build errors were along the lines of not being able to find Android Support library resources in the styles.xml. This also caused an issue where source files were not loading with intellisense or highlighting, so the error was even less obvious (see Xamarin.Tip – Fixing the Highlighting Drop In Your Xamarin.Android Projects).

When going into the nuget package manager and trying to manually add the Xamarin.Forms package, I was hit with this error:

Could not install package 'Xamarin.Android.Support.v4 23.3.0'. You are trying to install this package into a project that targets 'MonoAndroid,Version=v2.3'
...

The Solution

My first instinct was to check the Android target version in the project options to make sure that they were within the supported SDK range, and they were:
Screen Shot 2017-06-21 at 4.05.17 PM

Unlike Visual Studio 2017 (or any Windows VS with Xamarin), there was no “Compile using Android Version” picker. THIS field is what is really determines the projects target framework level, but VS for Mac doesn’t have it like it should (hope you’re reading this Microsoft!).

Let’s fix it manually:

  1. Unload your Android project by right clicking it in the Solution explorer and clicking “Unload”
  2. Open the Android project .csproj in the editor by right clicking > Tools > Edit.
  3. For each build configuration, add an explicit TargetFrameworkVersion that’s compatible. In my case that was v4.4
  4. Your .csproj should look like this:
    <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    ... 
      <PropertyGroup>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
        <ProjectGuid>{2BC9AAD5-D311-499E-8B06-A63CCB65C633}</ProjectGuid>
        <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
        <OutputType>Library</OutputType>
        <RootNamespace>AppName.Droid</RootNamespace>
        <AssemblyName>AppName.Droid</AssemblyName>
        <AndroidApplication>True</AndroidApplication>
        <AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
        <AndroidResgenClass>Resource</AndroidResgenClass>
        <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
        <MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
        <MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
        <AndroidUseLatestPlatformSdk>true</AndroidUseLatestPlatformSdk>
        <TargetFrameworkVersion>v4.4</TargetFrameworkVersion> <!-- THIS IS WHAT YOU NEED TO ADD OR CHANGE -->
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <Optimize>false</Optimize>
        <OutputPath>bin\Debug</OutputPath>
        <DefineConstants>DEBUG;</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <AndroidLinkMode>None</AndroidLinkMode>
        <TargetFrameworkVersion>v4.4</TargetFrameworkVersion><!-- THIS IS WHAT YOU NEED TO ADD OR CHANGE -->
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <OutputPath>bin\Release</OutputPath>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <AndroidManagedSymbols>true</AndroidManagedSymbols>
        <AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
        <TargetFrameworkVersion>v4.4</TargetFrameworkVersion><!-- THIS IS WHAT YOU NEED TO ADD OR CHANGE -->
      </PropertyGroup>
    
    ...
    </Project>
    
  5. Close the .csproj editor
  6. Reload the project by right clicking in the Solution Explorer and selecting Reload
  7. Retry adding the Xamarin.Forms nuget package to the project

Now you should be good to go. Did this not work for you? Leave a comment below and I’d be happy to help you fix this annoying issue as well!

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.

Xamarin.Tip – Mvvm Light and Dependency Injection

Inversion of Control and Dependency Injection are some design principles that help make our applications more flexible and scalable. They both help us separate our implementations and make it easy to substitute drastic changes to our implemented data or business logic whether it be for writing unit tests or product improvement.

Xamarin is a platform where IoC and DI fit extremely well. I’ve talked about this concept a few other times in both my blogs and videos about the Onion Architecture in Xamarin as well as how to call Platform Specific code from a Portable Class Library. You can find those posts and videos here:

  1. Onionizing Xamarin Part 6
  2. [VIDEO] Xamarin.Tips: Calling Platform-Specific Code from a PCL (Dependency Injection)

In this post, I want to talk about using DI with Mvvm Light at a VERY basic level.

First, let’s define an interface for a service we might use:

IUserService.cs

public interface IUserService
{
    Task<User> GetCurrentUserAsync();
}

Now let’s create two different implementations. One that will be the service used in the application and the other that will be used for testing.

UserService.cs

public class UserService : IUserService
{
    // makes a call to a web api to get a user
    public async Task<User> GetCurrentUserAsync()
    {
        using (var client = new HttpClient())
        {
            var response = await client.GetAsync("https://mywebapi.mydomain/api/currentuser");
            var content = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<User>(content);
        }
    }
}

TestUserServices.cs

public class TestUserService : IUserService
{
    public Task<User> GetCurrentUserAsync()
    {
        return Task.FromResult(new User { Name = "Test User" });
    }
}

Now we need a ViewModel that will use this service. We define a private readonly IUserService and then inject the implementation that we want in the constructor of the ViewModel.

CurrentUserViewModel.cs

public class CurrentUserViewModel : ViewModelBase
{
    // use the interface as the service and inject the implementation in the constructur
    private readonly IUserService _userService;
    private User _user;

    public User User
    {
        get
        {
            return _user;
        }
        set
        {
            Set(ref _user, value);
        }
    }

    public CurrentUserViewModel(IUserService userService)
    {
        _userService = userService;
    }

    public async Task UpdateUserAsync()
    {
        User = await _userService.GetCurrentUserAsync();
    }
}

Now let’s define an IoCConfig that handles registering dependencies and implementations.

IoCConfig.cs

public class IoCConfig
{
    public IoCConfig()
    {
        // use SimpleIoc from MvvmLight as our locator provider
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    }

    // register the real implementation
    public void RegisterServices()
    {
        SimpleIoc.Default.Register<IUserService, UserService>();
    }

    // register the test implementation
    public void RegisterTestServices()
    {
        SimpleIoc.Default.Register<IUserService, TestUserService>();
    }

    // register the view model
    public void RegisterViewModels()
    {
        SimpleIoc.Default.Register<CurrentUserViewModel>();
    }
}

Now that we can register our Services as well as our ViewModels, the dependency resolver from SimpleIoc can retrieve an instance of CurrentUserViewModel with whichever version of IUserService is registered depending on whether we call RegisterServices or RegisterTestServices.

Now we can retrieve our instance of the CurrentUserViewModel by calling

var currentUserViewModel = ServiceLocator.Current.GetInstance<CurrentUserViewModel>();

MvvmLight recommends using a ViewModelLocator to get the instance of your ViewModels:

ViewModelLocator.cs

public class ViewModelLocator
{
    private readonly IoCConfig _iocConfig;
    public CurrentUserViewModel CurrentUser
    {
        get
        {
            return ServiceLocator.Current.GetInstance<CurrentUserViewModel>();
        }
    }

    public ViewModelLocator()
    {
        _iocConfig = new IoCConfig();
        _iocConfig.RegisterServices();
        //_iocConfig.RegisterTestServices();
        _iocConfig.RegisterViewModels();
    }

}

It’s recommended to either create your ViewModelLocator at the app start up, or if you’re using Xamarin.Forms, register it as a Resource in your App.xaml

<Application ...     xmlns:locator="clr-namespace:YOUR_LOCATOR_LOCATION">
    <Application.Resources>
        <ResourceDictionary>
            <locator:ViewModelLocator x:Key="Locator"/>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Now in your XAML pages, you can automatically wire up your view model.

MainPage.xaml

<ContentPage ...     BindingContext="{Binding Source={StaticResource Locator}, Path=CurrentUser}"     Title="{Binding User.Name}">
...
</ContentPage>

In order to change to your testing data, you can just switch which call to your IoCConfig is made for registering your dependency without having to make any changes to any of your other layers or UI!

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.

Xamarin.Tip – Adding Dynamic Elevation to Your Xamarin.Forms Buttons

Before Reading

In a previous post, I talked about bringing Material Design to your iOS applications in Xamarin.Forms and adding drop shadows to them. You might want to read that here first: Xamarin.Tips – Creating a Material Design Button in iOS

In another post, we learned how to override the Android Button Elevations. We will be doing this in this post in order to set a dynamic elevation. You can read that here: Xamarin.Tips – Overriding Android Button Shadows/Elevation

Now in this post, we will combine these two concepts with a new custom Xamarin.Forms component called MaterialButton that will have a new Elevation property to control the elevation and shadow of the underlying button control.

The source code and an example can be found here: https://github.com/SuavePirate/MaterialButton

Using the existing code

You can of course use the code I wrote and put on GitHub for this. In order to use it, simply:

  1. Clone the repository at  https://github.com/SuavePirate/MaterialButton
  2. Include all 3 `src` projects in your Solution
  3. Reference the Shared project in all Xamarin.Forms and platform projects
  4. Reference the Android project in your Android projects
  5. Reference the iOS project in your iOS projects
  6. Use the control as below (see example projects to demo)

Reference the control in your XAML:

MainPage.xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:local="clr-namespace:MaterialButtonExample"              xmlns:suave="clr-namespace:SuaveControls.MaterialButton.Shared;assembly=SuaveControls.MaterialButton.Shared"              x:Class="MaterialButtonExample.MainPage">

	<suave:MaterialButton x:Name="MyButton"                            BackgroundColor="#03A9F4"                            TextColor="White"                            Text="Click to raise elevation"                            Elevation="1"                            VerticalOptions="Center"                            HorizontalOptions="Center"                           WidthRequest="300"                           Clicked="MyButton_Clicked"/>

</ContentPage>

MainPage.xaml.cs

namespace MaterialButtonExample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void MyButton_Clicked(object sender, EventArgs e)
        {
            MyButton.Elevation++;
        }
    }
}

In your iOS AppDelegate you’ll also need to call the Initialize method to ensure that the Custom renderer does not get excluded during linking:

AppDelegate.cs

namespace MaterialButtonExample.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            MaterialButtonRenderer.Initialize();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}

Now you can see your results!

Creating Your Own Material Design Button

First things first, let’s create our new Xamarin.Forms control before we implement our custom renderers:

MaterialButton.cs

namespace SuaveControls.MaterialButton.Shared
{
    public class MaterialButton : Button
    {
        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);
            }
        }
    }
}

Now let’s implement our iOS and Android custom renderers.

iOS:
MaterialButtonRenderer


[assembly: ExportRenderer(typeof(MaterialButton), typeof(MaterialButtonRenderer))]
namespace SuaveControls.MaterialButton.iOS
{
    public class MaterialButtonRenderer : ButtonRenderer
    {
        public static void Initialize()
        {
            // empty, but used for beating the linker
        }
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement == null)
                return;

        }

        public override void Draw(CGRect rect)
        {
            base.Draw(rect);
            UpdateShadow();
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if(e.PropertyName == "Elevation")
            {
                UpdateShadow();
            }
        }

        private void UpdateShadow()
        {

            var materialButton = (Shared.MaterialButton)Element;

            // Update shadow to match better material design standards of elevation
            Layer.ShadowRadius = materialButton.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;

        }
    }
}

Notice how we use the UpdateShadow method to use the Elevation property to set the ShadowRadius of our UIButton's Layer.

It’s important to also make the UpdateShadow call in the OnElementPropertyChanged so that we can dynamically change the Elevation property in our Xamarin.Forms control and see it reflected in the underlying UIButton.

Now let’s do it on Android:

MaterialButtonRenderer

[assembly: ExportRenderer(typeof(MaterialButton), typeof(MaterialButtonRenderer))]
namespace SuaveControls.MaterialButton.Droid
{
    public class MaterialButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
    {
        ///
<summary>
        /// Set up the elevation from load
        /// </summary>

        /// <param name="e"></param>
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement == null)
                return;

            var materialButton = (Shared.MaterialButton)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, materialButton.Elevation);
            ViewCompat.SetElevation(Control, materialButton.Elevation);
        }

        public override void Draw(Canvas canvas)
        {
            var materialButton = (Shared.MaterialButton)Element;
            Control.Elevation = materialButton.Elevation;
            base.Draw(canvas);
        }

        ///
<summary>
        /// Update the elevation when updated from Xamarin.Forms
        /// </summary>

        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if(e.PropertyName == "Elevation")
            {
                var materialButton = (Shared.MaterialButton)Element;
                ViewCompat.SetElevation(this, materialButton.Elevation);
                ViewCompat.SetElevation(Control, materialButton.Elevation);
                UpdateLayout();
            }
        }
    }
}

Just as mentioned in the iOS implementation, we need to make sure that we implement our changes in both the OnElementChanged method as well as the OnElementPropertyChanged event to ensure we are able to update our Elevation with MVVM.

Now we can use our control in our pages!

MainPage.xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:local="clr-namespace:MaterialButtonExample"              xmlns:suave="clr-namespace:SuaveControls.MaterialButton.Shared;assembly=SuaveControls.MaterialButton.Shared"              x:Class="MaterialButtonExample.MainPage">

	<suave:MaterialButton x:Name="MyButton"                            BackgroundColor="#03A9F4"                            TextColor="White"                            Text="Click to raise elevation"                            Elevation="1"                            VerticalOptions="Center"                            HorizontalOptions="Center"                           WidthRequest="300"                           Clicked="MyButton_Clicked"/>

</ContentPage>

MainPage.xaml.cs

namespace MaterialButtonExample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void MyButton_Clicked(object sender, EventArgs e)
        {
            MyButton.Elevation++;
        }
    }
}

In your iOS AppDelegate you’ll also need to call the Initialize method to ensure that the Custom renderer does not get excluded during linking:

AppDelegate.cs

namespace MaterialButtonExample.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            MaterialButtonRenderer.Initialize();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}

Now you can see your results!

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.