Customizing Xamarin Editor

Another post about editor. In my previous post. I’ve shared about how you can add a placeholder to Editor using Behavior. In this post we’ll go little bit further. We still have an editor with placeholder, but when user start typing on it, the placeholder will turn in to it’s title. So it will make our editor little bit more interactive to user. Let’s start with creating our custom editor.

Custom Editor

I don’t make any update about this class. This class is still exactly same like the same class in my older post. It has 2 additional properties, Placeholder and Placeholder Color. But Placeholder Color will have more rules in this editor, because we will use same color for placeholder and title. Here’s the unchanged code 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); }
    }
}
<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;">&#65279;</span>

Custom Content View

Now let’s handle the user interaction. We won’t be using behavior anymore in this post, because you can’t generate title just by using it. Instead, we’ll be making a custom content view. This is gonna look similar like when I created Entry with Title, but it will be little bit trickier, because unlike entry, editor doesn’t have placeholder by default. Let’s take a look at the xaml file.

TitledEditorCode

As you can see, we create similar xaml file like the entry. We have a label above custom editor to become the title. We also move all user interaction from behavior into this file. We handle The BindingContextChangedFocused, Unfocused and TextChanged event. With those four event handler, we’ll manage when the placeholder or the title is displayed. Let’s see the code behind it.

public partial class TitledEditor : ContentView
{
    public TitledEditor()
    {
        InitializeComponent();

        EditorContent.BindingContext = this;
        LabelTitle.BindingContext = this;
    }

    public static BindableProperty PlaceholderProperty =
        BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(TitledEditor), 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(TitledEditor), "#cccccc", BindingMode.TwoWay);

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

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

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

    public static BindableProperty FontSizeProperty =
        BindableProperty.Create(nameof(FontSize), typeof(int), typeof(TitledEditor), 16, BindingMode.TwoWay);

    public int FontSize
    {
        get => (int)GetValue(FontSizeProperty);
        set => SetValue(FontSizeProperty, value);
    }

    public static BindableProperty TextColorProperty =
        BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(TitledEditor), Color.Default, BindingMode.TwoWay);

    public Color TextColor
    {
        get { return (Color)GetValue(TextColorProperty); }
        set { SetValue(TextColorProperty, value); }
    }

    void Handle_ContainerFocused(object sender, FocusEventArgs e)
    {
        EditorContent.Focus();
    }

    void Handle_BindingContextChanged(object sender, EventArgs e)
    {
        if (string.IsNullOrEmpty(Text))
        {
            Text = Placeholder;
            LabelTitle.IsVisible = false;
            TextColor = Color.FromHex(PlaceholderColor);
        }
        else
        {
            LabelTitle.IsVisible = true;
            TextColor = Color.Default;
        }
    }

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

        if (Placeholder == Text)
        {
            Text = string.Empty;
        }
    }

    void Handle_Unfocused(object sender, FocusEventArgs e)
    {
        if (string.IsNullOrEmpty(Text))
        {
            Text = Placeholder;
            LabelTitle.IsVisible = false;
            TextColor = Color.FromHex(PlaceholderColor);
        }
    }

    void Handle_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (string.IsNullOrEmpty(Text))
        {
            LabelTitle.IsVisible = true;
            TextColor = Color.FromHex(PlaceholderColor);
        }
        else
        {
            if (Text != Placeholder)
            {
                LabelTitle.IsVisible = true;
                EditorContent.TextColor = Color.Default;
            }
            else
            {
                EditorContent.TextColor = Color.FromHex(PlaceholderColor);
            }
        }
    }
}

Let me walk you through it. First, when the binding context changed, it means when in initialized, it will check whether the editor have text on it or not. If it doesn’t have one, the placeholder will become the text with the color of placeholder color. We won’t have any title on it. But if it has text on it, the text color will be default and we will show the title with color of placeholder color. Basically, we do the similar checking to the other three events. The placeholder and title just keep switching place depend on the text of the editor and both of them have same color.

Titled Editor in action

It’s all it takes to make Editor with placeholder and title. And if you try to use the editor in one of you page, it will look like this.

Sample Code is available in my Github repo

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