Making Enum as Bindable Property

This post is actually just an update from my previous post about Entry with title. I’ve made some enhancement to optimize my titled entry. From several changes, I think this enum bindable property is the most important so I decided to make one post discussing about it. But in this post I will also review some other minor changes. So, Let’s begin.

Additional Properties and methods

I added two additional properties. The first is Text, for binding a text to the entry, and Entry keyboard to binding the type of the keyboard. Those two properties are two most essential features of entry that I previously forgot to add. For Text property, it’s just another simple string binding property, but for the keyboard, it’s little bit different. Because entry’s keyboard has several different types that developer can choose depend on what field the entry’s used for. So, I created an enum bindable property to tackle that issue. This is how the code looks like.

public partial class TitledEntry : ContentView
{
    string placeholder = string.Empty;

    public enum KeyboardEnum
    {
        Default,
        Text,
        Chat,
        Url,
        Email,
        Telephone,
        Numeric,
    }

    public TitledEntry()
    {
        InitializeComponent();

        EntryContent.BindingContext = this;
        LabelTitle.BindingContext = this;
        LabelTitle.Text = string.Empty;
    }

    public static BindableProperty PlaceholderProperty =
        BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(TitledEntry), null, BindingMode.TwoWay);

    public string Placeholder
    {
        get => (string)GetValue(PlaceholderProperty);
        set => SetValue(PlaceholderProperty, value);
    }

    public static BindableProperty TextProperty =
        BindableProperty.Create(nameof(Text), typeof(string), typeof(TitledEntry), null, BindingMode.TwoWay);

    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }

    public static BindableProperty KeyboardProperty =
        BindableProperty.Create(nameof(KeyboardProperty), typeof(KeyboardEnum), typeof(TitledEntry), KeyboardEnum.Default, BindingMode.TwoWay);

    public KeyboardEnum EntryKeyboard
    {
        get => (KeyboardEnum)GetValue(KeyboardProperty);
        set
        {
            SetValue(KeyboardProperty, value);
            SetKeyboard();
        }
    }

    void Handle_Focused(object sender, FocusEventArgs e)
    {
        LabelTitle.Text = Placeholder;

        if (EntryContent.Text == null || EntryContent.Text.Length == 0)
        {
            placeholder = EntryContent.Placeholder;
            EntryContent.Placeholder = string.Empty;
        }

    }

    void Handle_Unfocused(object sender, FocusEventArgs e)
    {
        if (EntryContent.Text == null || EntryContent.Text.Length == 0)
        {
            EntryContent.Placeholder = placeholder;
            LabelTitle.Text = string.Empty;
        }

    }

    void Handle_TextChanged(object sender, TextChangedEventArgs e)
    {
        var entry = sender as Entry;
        if (!string.IsNullOrEmpty(entry.Text))
        {
            LabelTitle.Text = Placeholder;
        }
    }

    void SetKeyboard()
    {
        switch (EntryKeyboard)
        {
            case KeyboardEnum.Default:
                EntryContent.Keyboard = Keyboard.Default;
                break;
            case KeyboardEnum.Text:
                EntryContent.Keyboard = Keyboard.Text;
                break;
            case KeyboardEnum.Chat:
                EntryContent.Keyboard = Keyboard.Chat;
                break;
            case KeyboardEnum.Url:
                EntryContent.Keyboard = Keyboard.Url;
                break;
            case KeyboardEnum.Email:
                EntryContent.Keyboard = Keyboard.Email;
                break;
            case KeyboardEnum.Telephone:
                EntryContent.Keyboard = Keyboard.Telephone;
                break;
            case KeyboardEnum.Numeric:
                EntryContent.Keyboard = Keyboard.Numeric;
                break;
            default:
                EntryContent.Keyboard = Keyboard.Default;
                break;
        }
    }
}

As you can see at the code, I create similar enum like the original entry’s keyboard and then I created a bindable property for that enum. And then I create the method SetKeyboard¬†to convert my enum to the real keyboard. I know I can use value converter, but I think it’s more simple this way.

Another method that I added is Text Changed event handling for the entry. I use this method to handle if entry text already has value from beginning.  And for note, I also make a change in xaml side. I turned the stack layout into grid to optimize the rendering performance of the xaml.

BindableEnum1

Sample Code is available in my Github repo

Turning Entry’s Placeholder into Title in Xamarin Forms

I really hope that this title is not misleading. I’m not sure ‘turning’ is the right word, but I don’t know better word to describe what I want to discuss in this post. As we know together, in Android Native app, we can have Entry component whose placeholder will turn into title with a little ‘going up’ animation when user start interact with it. And because you can only have it in Android, not in other platform, Xamarin Forms doesn’t support it, at least not yet. Unfortunately, in my current project I’m already required to create such a thing. So, this is how I do the work around, not perfect yet but not too bad either.

Custom Entry with Title

The first thing I do is creating a custom content view with a stack layout in it. The stack layout contains an Entry and a Label that will serve as Entry’s title. Why stack layout, not Grid? It’s not just for simplicity’s sake. Remember the ‘going up’ animation I discuss earlier? I want to create an effect like that, not as good as the original, but I think it’s good enough. When you hide something in stack layout and something trigger it to appear, it will create a little animation, up or down depend on the position. It also works the other way around, when you hide something, you’ll get similar animation. So, this how I put the Entry and Label on the xaml file.

TitledEntryCode1

Pay the on the Entry. It has event handler for Focused and Unfocused event, that where we gonna do the trick. And the code below is how the code behind looks like.

public partial class TitledEntry : ContentView
{
    string placeholder = string.Empty;

    public TitledEntry()
    {
        InitializeComponent();

        if (EntryContent.Text == null || EntryContent.Text.Length == 0)
        {
            LabelTitle.IsVisible = false;
        }

        EntryContent.BindingContext = this;
        LabelTitle.BindingContext = this;
    }

    public static BindableProperty PlaceholderProperty =
        BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(TitledEntry), null, BindingMode.TwoWay);

    public string Placeholder
    {
        get => (string)GetValue(PlaceholderProperty);
        set => SetValue(PlaceholderProperty, value);
    }

    void Handle_Focused(object sender, FocusEventArgs e)
    {
        LabelTitle.IsVisible = true;

        if (EntryContent.Text == null || EntryContent.Text.Length == 0)
        {
            placeholder = EntryContent.Placeholder;
            EntryContent.Placeholder = string.Empty;
        }

    }

    void Handle_Unfocused(object sender, FocusEventArgs e)
    {
        if (EntryContent.Text == null || EntryContent.Text.Length == 0)
        {
            EntryContent.Placeholder = placeholder;
            LabelTitle.IsVisible = false;
        }

    }
}

So all I do is just giving condition about Entry’s text length and stack layout will do the rest. If there’s one thing you need to remember is setting the binding context of the Entry and Label to current class like I do in the constructor or you won’t be able to bind them from view model.

Let’s use it

Now that we already have our custom entry ready, let see it in action. I create a view model contains 2 strings called Username and Email and I set a view model as the binding context for the class whose xaml file you can see below.

TitledEntryCode2

And if try to run the solution, you’ll have Entries like the real Android’s Entry.

 

Update : Making Enum as Bindable Property

Sample Code is available in my Github repo

 

Get rid of underline from Entry in Xamarin Android

Have you ever felt annoyed by the entry’s underline on Android? I know it’s there by default, and the designers in Google’s headquarter must have reason for it. But. sometime it just doesn’t suit the design of your app, so you just need to get rid of it. I once tried to do it, I searched on the internet, but all I found was how to do it in Android Studio. There’s bunch examples how to do it if you’re developing an Android app in Android Studio, all you have to do is just doing some simple search on internet. But, what if you’re developing a Cross Platform app with Xamarin? Nah, it will be bit more complicated.

Create Background for Entry

So, this is what I did with my project. I created an .axml file and I put it in drawable folder in Droid project. I will use this file to be the background of the entry. The shape of the entry will be a transparent rectangle. Why’s transparent? Because it’s Android’s default background color. Why’s rectangle? It’s just for simplicity and normality sake, you can make a circle instead, if you want. The following code is Background Entry code.

<?xml version="1.0" encoding="UTF-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
    <shape android:shape="rectangle">
      <solid android:color="#00ffffff"/>
    </shape>
  </item>
</selector>

Let’s discuss it a little bit. The shape is rectangle, no question about it. And the color is #00ffffff. As you know that #ffffff is Hex code for the color white. And the two zeroes in front of it is the degree of the transparency. The smaller the number, the more transparent the color. So, if you don’t want it to be too transparent, you can just add the number, like 20 or 70. But for this example I will use the ’00’ number.

Custom Renderer

Now after we have new background for our Entry, all we need to do just create custom renderer to apply the background. Here’s the code for the custom entry renderer.

public class CustomEntryRenderer : EntryRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);
        this.Control.Background = Forms.Context.GetDrawable(Resource.Drawable.BackgroundEntry);
        Control?.SetPadding(20, 20, 20, 20);
    }
}

This method has one weakness, we don’t actually remove the underline, we just cover it with a background. So, the underline is still there, you just can’t see it anymore. It makes an awkward appearance that the text user inputing will slightly closer to the top. It’s vertically above the middle. And it also look too close to the left edge. So, I set some padding to make sure the the text of the entry will be in right place.

Let’s try it out

Now that we have our Entry ready, let’s try it out. When you’re using an Entry in a project, usually when you’re creating some kind of a form, there will be many states of the entry. For example, one normal state of a blank entry, or a disable entry that require user to fill other field first, or an error state when user submit the form but left the requirement field empty. To try out that condition, I created a simple app to show different state of an entry. Here’s the xaml code.

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" Title="ColorfulEntry" xmlns:control="clr-namespace:Testing.CustomControls" x:Class="Testing.Views.ColorfulEntry">
	<ContentPage.Content>
         <StackLayout VerticalOptions="StartAndExpand" Padding="30,30,30,0">
            <control:CustomEntry Placeholder="This is normal Entry"/>
            <control:CustomEntry Placeholder="This entry is disabled" IsEnabled="false" BackgroundColor="Gray"/>
            <control:CustomEntry Placeholder="This field must be filled" BackgroundColor="Red"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Entry1