Adding Placeholder to Editor

As we know, in Xamarin, Editor, just like Entry, is UI component where user can type the input they want. Unlike Entry, Editor supports multilines input, but as a downside, it doesn’t have placeholder, a hint text about what user should input. But sometime in projects, we need multilines user entry that has placeholder. And to do that, we need some kind of custom Editor that accommodate placeholder as one if its properties. How to do that is what we’ll discuss in this post.

Custom Editor

In Entry, we have properties like Placeholder and Placeholder color. So we need to add those two properties in our Custom Editor. And of  course, it should be bindable as well. The following code is the example of the Custom Editor class.

public class CustomEditor : Editor
{
    public static BindableProperty PlaceholderProperty =
        BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(CustomEditor), string.Empty, BindingMode.TwoWay);
    public string Placeholder
    {
        get { return (string)GetValue(PlaceholderProperty); }
        set { SetValue(PlaceholderProperty, value); }
    }

    public static BindableProperty PlaceholderColorProperty =
        BindableProperty.Create(nameof(PlaceholderColor), typeof(string), typeof(CustomEditor), "#cccccc", BindingMode.TwoWay);

    public string PlaceholderColor
    {
        get { return (string)GetValue(PlaceholderColorProperty); }
        set { SetValue(PlaceholderColorProperty, value); }
    }
}

In that example, the default value of the placeholder will be empty string, and the default color of the placeholder will be color with hex code #cccccc, it’s just a gray color I picked randomly. For the next step, we will manage how the editor will behave.

Editor Behavior

The second step, we’ll create a behavior class for our custom editor. This class’s task is mainly manage when the placeholder will appear and when it will be gone. Because originally Editor doesn’t have a place to show a placeholder, so we will use Text property of the Editor, to show  up our placeholder.

public class EditorBehavior : Behavior
{
    protected override void OnAttachedTo(CustomEditor editor)
    {
        editor.BindingContextChanged += OnBindingContextChanged;
        editor.Focused += OnEditorFocused;
        editor.Unfocused += OnEditorUnFocused;
        base.OnAttachedTo(editor);
   }

    protected override void OnDetachingFrom(CustomEditor editor)
    {
        editor.BindingContextChanged -= OnBindingContextChanged;
        editor.Focused -= OnEditorFocused;
        editor.Unfocused -= OnEditorUnFocused;
        base.OnDetachingFrom(editor);
    }

    void OnBindingContextChanged(object sender, EventArgs args)
    {
        CustomEditor editor = sender as CustomEditor;
        if (string.IsNullOrEmpty(editor.Text))
        {
            editor.Text = editor.Placeholder;
            editor.TextColor = Color.FromHex(editor.PlaceholderColor);
        }
    }

    void OnEditorFocused(object sender, FocusEventArgs args)
    {
        CustomEditor editor = sender as CustomEditor;
        string placeholder = editor.Placeholder;
        string text = editor.Text;

        if (placeholder == text)
        {
            editor.Text = string.Empty;
        }
        editor.TextColor = Color.Default;
    }

    void OnEditorUnFocused(object sender, FocusEventArgs args)
    {
        CustomEditor editor = sender as CustomEditor;
        string placeholder = editor.Placeholder;
        string text = editor.Text;

        if (string.IsNullOrEmpty(text))
        {
            editor.Text = placeholder;
            editor.TextColor = Color.FromHex(editor.PlaceholderColor);
        }
        else
        {
            editor.TextColor = Color.Default;
        }
    }
}

This behavior, in nutshell, has only three events. First, when its Binding Context changed, it’s when the view model is set, we will also set our placeholder to the editor if it doesn’t have any text. The next two are on Focused and Unfocused, when user start and finish editing. The condition is the same, if it has any text, let it be. If it doesn’t, set the placeholder.

Let’s try it out!

To see how it works, all we have to do is attaching the behavior to the Custom Editor. In this example, I will attach it from the xaml. Here’s the sample code how to do it.


    
        
            
                
            
        
        
    

And here’s the sample code of the view model that bind to the xaml file.

 

public class EditorViewModel : BaseViewModel
{
    protected string placeholder;
    public string Placeholder
    {
        get => placeholder;
        set => SetProperty(ref placeholder, value);
    }

    protected string text;
    public string Text
    {
        get => text;
        set => SetProperty(ref text, value);
    }

    public EditorViewModel()
    {
        Placeholder = "Write a Message";
    }
}

If you run the code, at first you’ll have an editor with a gray text on it. When you start typing, the gray text will be gone and replaced by you text with a normal text color.

 

Update : Customizing Xamarin Editor 

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

Add Custom Back Button on Android

Ok, first of all, this post is actually a complementary post of my previous post, Changing Navigation Bar Color Dynamically. In that post we discussed about how to change the color of the navigation bar when we navigate to different menu. At the end of that post, I gave external reference about how to override back button. That overriding back button method solved almost all the problem we had about back button. Unless one thing. In Android, changing the navigation bar color give us a navigation bar without back button. We still could navigate with device back button, but normally an Android app will have back button on its navigation bar. So, in this post we will discuss how to add our own back button and we will override it, just like we did in previous post.

Accessing Toolbar from Page Renderer

To create custom back button and adding it to the page, obviously we gonna need custom renderer. But, there’s one little problem. In Android, the navigation bar is actually a toolbar and we can not access it freely from the renderer. We need to have access first to the activity, and then we access the toolbar. And in doing so, I got tremendous help from Xamarin Forum. We just to add this following codes to MainActivity.cs

private static MainActivity _this;
public static View RootFindViewById<T>(int id) where T : View
{
    return _this.FindViewById<T>(id);
}
public MainActivity()
{
    _this = this;
}

Customize Android Logo

After we’ve done with the MainActivity, now we’ll create the page renderer. The page renderer will render the CoolContentPage that we discuss in the previous post. In this page renderer, we’ll customize the Android Logo. Android Logo is toolbar component that’s placed between back button and toolbar title. And with we having no back button in this case, android logo become most left placed component on the toolbar. And, fortunately, android logo is basically an ImageView, little bit different with the regular ImageView, but still, like an ImageView, we can set an image and adding listener to it. And this is exactly what we gonna do. First, add an Android back button image to Drawable folder, and then create CoolContentPage custom renderer with the following code.

public class CoolContentPageRenderer : PageRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
    {
        base.OnElementChanged(e);
        if (((CoolContentPage)Element).EnableBackButtonOverride)
        {
            SetCustomBackButton();
        }
    }
    private void SetCustomBackButton()
    {
        //accessing toolbar from MainActivity
        var toolbar = (Toolbar)MainActivity.RootFindViewById<Toolbar>(Resource.Id.toolbar);

        // set Android Logo's image
        toolbar.SetLogo(Resource.Drawable.AndroidBackButton);

        for (int i = 0; i < toolbar.ChildCount; i++)
        {
            var item = toolbar.GetChildAt(i);
            //if Android Logo
            if (item.GetType() == typeof(AppCompatImageView))
            {
                AppCompatImageView image = (AppCompatImageView)item;
                image.Click += (sender, e) =>
                {
                    if (((CoolContentPage)Element)?.CustomBackButtonAction != null)
                    {
                        ((CoolContentPage)Element)?.CustomBackButtonAction.Invoke();
                    }
                };
            }
        }
    }
}

Navigation Handling

Now that’s all set, we just need to do little update to the content page. Just like I stated earlier, this post is complementary of my previous post, so I’ll give example from my previous post as well. In that post, we created breakfast page menu with overriding back button. And customizing android logo will have massive effect to breakfast page.

Let’s take one page for example. Let say we navigate to Sandwich Page, of course it gonna have customized android logo. And then we navigate to other page from Sandwich Page, that page will still have the android logo. Event hough we don’t inherit that CoolContentPage. Yes, once we set it, Android logo stays still. It will be a problem if the next page already has natural back button, so the page will have two back buttons. And even worse, the android logo back button won’t even follow the natural order of navigation stack. It will still execute the listener we set on page renderer.

To avoid such kind of problem, we need to update our breakfast page. Here’s the example how I did it.

public partial class BreakfastSandwich : CoolContentPage
{
    public BreakfastSandwich()
    {
        InitializeComponent();
        if (EnableBackButtonOverride)
        {
            this.CustomBackButtonAction = () =>
            {
                 var currentpage = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();
                 // if root page
                 if(currentpage.GetType() ==typeof(BreakfastSandwich))
                 {
                    Application.Current.MainPage = new BreakfastPage();
                 }
                 else
                 {
                    Navigation.PopAsync();
                 }
            };
        }
    }
}

And for any pages that Breakfast Sandwich navigate into, we have to remove the natural back button, so we don’t have double back button on the toolbar.  We’ll only do it in Android, because Android Logo won’t have any effect on iOS platform. Here’s the example.

public partial class MoreSandwichPage : ContentPage
{
    public MoreSandwichPage()
    {
        InitializeComponent();
        if (Device.RuntimePlatform == Device.Android)
        {
            NavigationPage.SetHasBackButton(this, false);
        }
    }
}

Note : For simplicity sake, I used raw icon I got from FlatIcon, in real project you should use customize back button icon that has space after the arrow, in order to make it look like real Android back button.

 

Credit:

  • Icon from FlatIcon, Left Arrow icon by Google
  • Override Navigation Back Button by Udara Alwis (blog)
  • Access Toolbar from Page Renderer by ChaseFlorell (Xamarin Forum)
  • Get Android Logo from Toolbar by Mike M. (StackOverflow)

Bindable Check Box and Radio Button

In my previous post, Check Box in Xamarin Forms, we’ve discussed about how to create checkbox, or at least something that has effect like check box in Xamarin Forms. I created that post based on my previous project that only required me to make a simple and plain check box list. In my current project, the requirement has escalated. I need to create several list of check box and radio button, and they need to talk to each other. By talk to each other, I mean if the user do something to one of them, let say the radio button, it will be affect the check box, and/or vice versa.

It will be complicated for me if I just use the method that I’ve used in previous project. I need some  improvements.  So, I created custom class of check box and radio button with some bindable properties on it. That custom class enable me to re-use the check box and radio button anytime I want, and then their bindable properties make it easy for me to manipulate it from view model. I also, once again, utilize Flow List View in this project to simplify user interaction process. Now, let’s get started.

Custom Content View

The first step we gonna do is creating custom content view of the check box and radio button. The following code is the content view of the check box.
BindableCode1

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class BindableCheckBox : ContentView, INotifyPropertyChanged
{
    public BindableCheckBox()
    {
        InitializeComponent();
    }

    public static BindableProperty IconSourceProperty =
        BindableProperty.Create(nameof(IconSource), typeof(string), typeof(BindableCheckBox), null, BindingMode.TwoWay);

    public string IconSource
    {
        get => (string)GetValue(IconSourceProperty);
        set => SetValue(IconSourceProperty, value);
    }

    public static BindableProperty MenuTitleProperty =
        BindableProperty.Create(nameof(MenuTitle), typeof(string), typeof(BindableCheckBox), null, BindingMode.TwoWay);

    public string MenuTitle
    {
        get => (string)GetValue(MenuTitleProperty);
        set => SetValue(MenuTitleProperty, value);
    }

    public static BindableProperty TitleColorProperty =
         BindableProperty.Create(nameof(TitleColor), typeof(Color), typeof(BindableCheckBox), Color.Transparent, BindingMode.TwoWay);

    public Color TitleColor
    {
        get => (Color)GetValue(TitleColorProperty);
        set => SetValue(MenuTitleProperty, value);
   }
}

As you can see above, the class contains some properties that are bind to xaml file. And we will be bind that properties from view model later. And here’s the code of the custom radio button. It’s actually pretty similar.

BindableCode2

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class BindableRadioButton : ContentView, INotifyPropertyChanged
{
    public BindableRadioButton()
    {
        InitializeComponent();
    }

    public static BindableProperty IconSourceProperty =
        BindableProperty.Create(nameof(IconSource), typeof(string), typeof(BindableRadioButton), null, BindingMode.TwoWay);

    public string IconSource
    {
        get => (string)GetValue(IconSourceProperty);
        set => SetValue(IconSourceProperty, value);
    }

    public static BindableProperty MenuTitleProperty =
        BindableProperty.Create(nameof(MenuTitle), typeof(string), typeof(BindableRadioButton), null, BindingMode.TwoWay);

    public string MenuTitle
    {
        get => (string)GetValue(MenuTitleProperty);
        set => SetValue(MenuTitleProperty, value);
    }
}

View Model

Now, we move on to the second step. We need to create view model for each of them, the check box and radio button, to data bind their properties and notify if there is any change to it. The following codes are the example of the radio button and check box view model.

public class PickingColorViewModel : BaseViewModel
{
    protected ObservableCollection listRadioButton;
    public ObservableCollection ListRadioButton
    {
        get => listRadioButton;
        set => SetProperty(ref listRadioButton, value);
    }

    protected BindableRadioButtonViewModel selectedRadioButton;
    public BindableRadioButtonViewModel SelectedRadioButton
    {
        get => selectedRadioButton;
        set => SetProperty(ref selectedRadioButton, value);
    }

    public ICommand RadioButtonTappedCommand { get; set; }

    protected ObservableCollection listCheckBox;
    public ObservableCollection ListCheckBox
    {
        get => listCheckBox;
        set => SetProperty(ref listCheckBox, value);
    }

    protected BindableCheckBoxViewModel selectedCheckBox;
    public BindableCheckBoxViewModel SelectedCheckBox
    {
        get => selectedCheckBox;
        set => SetProperty(ref selectedCheckBox, value);
    }

    public ICommand CheckBoxTappedCommand { get; set; }

    public PickingColorViewModel()
    {
        ListRadioButton = new ObservableCollection()
        {
            new BindableRadioButtonViewModel(){ IsSelected = true, EnableProperty = true, MenuTitle = "Enable" },
            new BindableRadioButtonViewModel(){ IsSelected = false, EnableProperty = false, MenuTitle = "Disable" }
        };

        ListCheckBox = new ObservableCollection()
        {
            new BindableCheckBoxViewModel() { IsEnabled = true, IsChecked = false, MenuTitle ="Cyan", DefaultTitleColor = Color.Cyan },
            new BindableCheckBoxViewModel() { IsEnabled = true, IsChecked = false, MenuTitle ="Indigo", DefaultTitleColor = Color.Indigo },
            new BindableCheckBoxViewModel() { IsEnabled = true, IsChecked = false, MenuTitle ="Firebrick", DefaultTitleColor = Color.Firebrick },
        };

        RadioButtonTappedCommand = new Command(RadioButtonTapped);
        CheckBoxTappedCommand = new Command(CheckBoxTapped);
    }

    private void CheckBoxTapped()
    {
        foreach (BindableCheckBoxViewModel check in ListCheckBox)
        {
            if (check.MenuTitle == SelectedCheckBox.MenuTitle)
                check.IsChecked = !check.IsChecked;
        }
    }

    private void RadioButtonTapped()
    {
        foreach (BindableRadioButtonViewModel radio in ListRadioButton)
        {
            if (radio.MenuTitle == SelectedRadioButton.MenuTitle)
                radio.IsSelected = true;
            else
                radio.IsSelected = false;

        }

        foreach (BindableCheckBoxViewModel check in ListCheckBox)
        {
            check.SetEnable(SelectedRadioButton.EnableProperty);
        }
    }
}

From the view model codes above, you can see that we will manipulate the image, text color and inability of the check box. And for the radio button, we’ll only manipulate the image, but it also has EnableProperty that we’ll use to manipulate the inability of the check box. I use those properties just for example. If you want to bind other properties such as Background color or Text Size, all you have to do is just adding more properties to view model and custom content view. The name of property in view model and content view have to be same. Let say, your property in view model is MenuBackgroundColor, in content view it has to be MenuBackgroundColor as well, or it won’t be bind. Now. let’s see how we’ll use it.

Implementation

For example of using this bindable check box and radio button, I’ve created a simple app that has list of check box and radio button. Each check box will have different color and the radio button will determine the inability of the check box. And I will put each list in a flow list view and let it handle the user interaction. Here’s the xaml code looks like.

BindableCode3

Now, after we done with the xaml, we’ll create the view model that will manipulate the properties that already bind in xaml. Here’s the code of the view model.

public class PickingColorViewModel : BaseViewModel
{
    protected ObservableCollection listRadioButton;
    public ObservableCollection ListRadioButton
    {
        get => listRadioButton;
        set => SetProperty(ref listRadioButton, value);
     }

    protected BindableRadioButtonViewModel selectedRadioButton;
    public BindableRadioButtonViewModel SelectedRadioButton
    {
        get => selectedRadioButton;
        set => SetProperty(ref selectedRadioButton, value);
    }

    public ICommand RadioButtonTappedCommand { get; set; }

    protected ObservableCollection listCheckBox;
    public ObservableCollection ListCheckBox
    {
        get => listCheckBox;
        set => SetProperty(ref listCheckBox, value);
    }

    protected BindableCheckBoxViewModel selectedCheckBox;
    public BindableCheckBoxViewModel SelectedCheckBox
    {
        get => selectedCheckBox;
        set => SetProperty(ref selectedCheckBox, value);
    }

    public ICommand CheckBoxTappedCommand { get; set; }

    public PickingColorViewModel()
    {
       ListRadioButton = new ObservableCollection()
       {
             new BindableRadioButtonViewModel(){ IsSelected = true, EnableProperty = true, MenuTitle = "Enable" },
             new BindableRadioButtonViewModel(){ IsSelected = false, EnableProperty = false, MenuTitle = "Disable" }
       };

       ListCheckBox = new ObservableCollection()
       {
            new BindableCheckBoxViewModel() { IsEnabled = true, IsChecked = false, MenuTitle ="Cyan", DefaultTitleColor = Color.Cyan },
            new BindableCheckBoxViewModel() { IsEnabled = true, IsChecked = false, MenuTitle ="Indigo", DefaultTitleColor = Color.Indigo },
            new BindableCheckBoxViewModel() { IsEnabled = true, IsChecked = false, MenuTitle ="Firebrick", DefaultTitleColor = Color.Firebrick },
       };

       RadioButtonTappedCommand = new Command(RadioButtonTapped);
       CheckBoxTappedCommand = new Command(CheckBoxTapped);
    }

    private void CheckBoxTapped()
    {
        foreach(BindableCheckBoxViewModel check in ListCheckBox)
        {
            if (check.MenuTitle == SelectedCheckBox.MenuTitle)
                check.IsChecked = !check.IsChecked;
        }
    }

    private void RadioButtonTapped()
    {
        foreach(BindableRadioButtonViewModel radio in ListRadioButton)
        {
            if (radio.MenuTitle == SelectedRadioButton.MenuTitle)
                radio.IsSelected = true;
            else
                radio.IsSelected = false;
        }

        foreach(BindableCheckBoxViewModel check in ListCheckBox)
        {
            check.SetEnable(SelectedRadioButton.EnableProperty);
        }
    }
}

Credit:

  • Check Box Icon from FlatIcon, Checked Check Box icon by Google, Empty Check Box icon by Dave Gandy
  • Radio Button Icon from clker by Zorik
  • Flow List View by Daniel Luberda (github)

Sample Code is available in my Github repo