Adding Authorization Header in Web View

So, this is my second post about web view. Still from my last project, I had to deal with some problems regarding web view. One of them was adding authorization header in web view. When I look up on net, there were so many answers, but most of them didn’t work for me. So, I thought I should share which method that works for me.

Android Web View Renderer

In Android, I simply put the Authorization header on web view renderer. In some forums, there’re some different opinions about where we should put this header, like put it in web chrome client, but those methods didn’t work in my case. So, this is how I added the authorization header in my project.

public class HybridWebViewRenderer : WebViewRenderer
{
    public HybridWebViewRenderer(Context context) : base(context)
    { }
    public HybridWebViewRenderer(){ }

    protected override void OnElementChanged(ElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        var webView = Control as Android.Webkit.WebView;
        var Token = Settings.AccessToken;

        Dictionary headers = new Dictionary()
        {
            {"Authorization", "Bearer " + Token }
        };
        this.Control.LoadUrl(Control.Url, headers);
    }

}

I put it on OnElementChanged method, as a result, whenever the user navigate to other page in web view, causing the url changed, this method will be fired. So, the header will be added whenever user make new request in web view.

iOS Web View Delegate

Different from it’s Android counter part, we can’t add the Authorization header on web view renderer in iOS, we have to put it on web view delegate. In iOS web view delegate, there’s a method called ShouldStartLoad

Just like its name, this method is fired whenever the web view load new page. So, it’s actually pretty similar with the Android, but the way this method works is little bit different. In Nutshell, what we gonna do in this method are :

  1. Checking if the request has Authorization Header
  2.  If not, Copy the request and then add the header to the copied request.
  3. Cancel the original request without Authorization Header
  4. If the request already has the header, just let it through

So, this how it looks like in real code.

public class HybridWebViewRenderer : WebViewRenderer
{
    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        Delegate = new HybridUiWebViewDelegate(this);

        this.ScrollView.DecelerationRate = UIScrollView.DecelerationRateNormal;
        var webView = e.NewElement as HybridWebView;
    }

}

public class HybridUiWebViewDelegate : UIWebViewDelegate
{
    public override bool ShouldStartLoad(UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType)
    {
        if (!request.Headers.ContainsKey(new NSString("Authorization")))
        {
            var copy = request.MutableCopy() as NSMutableUrlRequest;
            var token = Settings.AccessToken;

            NSMutableDictionary dic = new NSMutableDictionary();
            dic.Add(new NSString("Authorization"), new NSString("Bearer " + token));
            copy.Headers = dic;

           string currentUrl = request.Url.ToString();

           if (currentUrl.ToLower() != StaticVariables.CurrentUrl.ToLower())
           {
               StaticVariables.CurrentUrl = currentUrl;
               StaticVariables.NavigationStack.Add(currentUrl);
           }

           webView.LoadRequest(copy);

           return false;
      }
      return true;
    }
}

Hope this method also works for you.

Nested Checkbox in Xamarin Forms

We already have two posts discussing about check box, but I wanna to take it one step further. So, in this post we’ll discuss one of the most common behavior that a check box has. Let’s say we have a list of check boxes, and every one of them, the check box, also have list of check boxes of their own, or in short, a list of nested check boxes. In this nested check box, when we check the parent box, all the children boxes will be automatically checked as well. On the contrary, when on of child is unchecked, the parent will be automatically unchecked as well. So, we’ll be discussing how that process can be executed in Xamarin forms, started from the custom content view of the check box.

Parent and Child Check Box

Just like the previous post about check box, I created custom content view of check box. Because we gonna have two different check box, so I created two different content view, one for parent, one for the child. And to distinguish between those two check boxes, I put the box in the left for parent, and in the right for the child. Here’s how the parent check box content view looks like

NestedCode1

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

    public static BindableProperty IconSourceProperty =
        BindableProperty.Create(nameof(IconSource), typeof(string), typeof(ChildrenCheckBox), 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(ChildrenCheckBox), null, BindingMode.TwoWay);

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

And the following code is the Parent Check box code.

NestedCode2

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

    public static BindableProperty IconSourceProperty =
        BindableProperty.Create(nameof(IconSource), typeof(string), typeof(ParentCheckBox), 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(ParentCheckBox), null, BindingMode.TwoWay);

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

Parent and Children View Model

After done with the UI, now we’re talking about the business process, about the view model. Just like the content view, we also have two kind view model, one for the parent, and one for the children. Let’s started with the parent. In the parent view model, beside all standard methods, we gonna have two main method. First, SetParent. So, we’ll receive list of children check boxes as parameter and then in this method we’ll set the parent of this check boxes to the current parent view model. The second method is UpdateChildren, is where we set the all children checked when the parent is also checked.

public class ParentCheckboxViewModel : BaseViewModel
{
    protected bool isChecked;
    public bool IsChecked
    {
        get => isChecked;
        set
        {
            SetProperty(ref isChecked, value);
            UpdateChildren();
            UpdateImageSource();
        }
    }

    protected string menuTitle;
    public String MenuTitle
    {
        get => menuTitle;
        set => SetProperty(ref menuTitle, value);
    }

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

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

    protected ChildrenCheckboxViewModel selectedChild;
    public ChildrenCheckboxViewModel SelectedChild
    {
        get => selectedChild;
        set => SetProperty(ref selectedChild, value);
    }

    public ICommand ChildTappedCommand { get; set; }

    public ParentCheckboxViewModel(ObservableCollection _children)
    {
        this.Children = _children;
        ChildTappedCommand = new Command(ChildTapped);
        IsChecked = false;
        SetParent();
    }

    private void ChildTapped()
    {
        foreach(ChildrenCheckboxViewModel child in Children)
        {
            if (child.MenuTitle == SelectedChild.MenuTitle)
                child.IsChecked = !child.IsChecked;
        }
    }

    protected void SetParent()
    {
        foreach(ChildrenCheckboxViewModel child in Children)
        {
            child.Parent = this;
            child.IsChecked = false;
        }
    }

    protected void UpdateChildren()
    {
        if(IsChecked)
        {
            foreach(ChildrenCheckboxViewModel child in Children)
            {
                child.IsChecked = true;
            }
        }
    }

    protected void UpdateImageSource()
    {
        if (IsChecked)
        {
            IconSource = "CheckBox.png";
        }
        else
        {
            IconSource = "CheckBoxEmpty.png";
        }
    }
}

In Children view model, beside all the basic methods, we have one important method, UpdateParent. In this method, we’ll uncheck the parent if the current children is getting unchecked by user.

public class ChildrenCheckboxViewModel : BaseViewModel
{
    protected bool isChecked;
    public bool IsChecked
    {
        get => isChecked;
        set
        {
            SetProperty(ref isChecked, value);
            UpdateImageSource();
            UpdateParent();
        }
    }

    protected string menuTitle;
    public String MenuTitle
    {
        get => menuTitle;
        set => SetProperty(ref menuTitle, value);
    }

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

    protected ParentCheckboxViewModel parent;
    public ParentCheckboxViewModel Parent
    {
        get => parent;
        set => SetProperty(ref parent, value);
    }

    protected void UpdateImageSource()
    {
        if (IsChecked)
        {
            IconSource = "CheckBox.png";
        }
        else
        {
            IconSource = "CheckBoxEmpty.png";
        }
    }

    protected void UpdateParent()
    {
        if (!IsChecked)
        {
            Parent.IsChecked = false;
        }
    }
}

Let’s try it out

Now that we’ve finished all preparation, let’s put all of them in a real case. So, I’ve created a simple program that utilize the parent and children check box. Here’s the code.

NestedCode3

 

public class NestedCheckboxViewModel: BaseViewModel
{
    protected ObservableCollection listChildren1;
    public ObservableCollection ListChildren1
    {
        get => listChildren1;
        set => SetProperty(ref listChildren1, value);
    }

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

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

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

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

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

    protected ParentCheckboxViewModel selectedParent;
    public ParentCheckboxViewModel SelectedParent
    {
        get => selectedParent;
        set => SetProperty(ref selectedParent, value);
    }

    public ICommand ParentTappedCommand { get; set; }

    public NestedCheckboxViewModel()
    {
        ListChildren1 = new ObservableCollection()
        {
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 1.1" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 1.2" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 1.3" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 1.4" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 1.5" },
        };

        ListChildren2 = new ObservableCollection()
        {
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 2.1" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 2.2" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 2.3" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 2.4" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 2.5" },
        };

        ListChildren3 = new ObservableCollection()
        {
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 3.1" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 3.2" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 3.3" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 3.4" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 3.5" },
        };

        ListChildren4 = new ObservableCollection()
        {
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 4.1" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 4.2" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 4.3" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 4.4" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 4.5" },
        };

        ListChildren5 = new ObservableCollection()
        {
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 5.1", },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 5.2" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 5.3" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 5.4" },
            new ChildrenCheckboxViewModel(){ MenuTitle = "Child CheckBox 5.5" },
        };

        ListParent = new ObservableCollection()
        {
            new ParentCheckboxViewModel(ListChildren1){ MenuTitle = "Parent Checkbox 1" },
            new ParentCheckboxViewModel(ListChildren2){ MenuTitle = "Parent Checkbox 2" },
            new ParentCheckboxViewModel(ListChildren3){ MenuTitle = "Parent Checkbox 3" },
            new ParentCheckboxViewModel(ListChildren4){ MenuTitle = "Parent Checkbox 4" },
            new ParentCheckboxViewModel(ListChildren5){ MenuTitle = "Parent Checkbox 5" },
        };

        ParentTappedCommand = new Command(ParentTapped);
    }

    private void ParentTapped()
    {
        foreach(ParentCheckboxViewModel parent in ListParent)
        {
            if (parent.MenuTitle == SelectedParent.MenuTitle)
                parent.IsChecked = !parent.IsChecked;
        }
    }
}

If you run the code, you’ll have a list of nested check boxes. And then if you check one of the parent, all the children boxes will be checked, and if you uncheck one of the children, the parent will be automatically uncheck.

Credit:

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

Handle Button Click Event on Web View

In my last project, I was required to load a web view from internet. It was piece of cake, at least what it look like at first. But then it wasn’t that simple anymore when I need to add listener event in C# based from what user did in web view. Let say, the user click a button, it would do whatever it supposed to do in web, but I also had to do something accordingly in my xamarin app based on what button user just clicked. I wasn’t sure if that was even possible, but after wandering all day long in stackoverflow, I found solution for both Android and iOS.

Android Web View Renderer

We move to Android project first. In this project we need create three classes. First, of course the renderer of our web view. Second is web view client, this where we gonna inject our event to html, in this case we will inject it to a button. And then the third is web chrome client where we’ll put event listener to our injected event.

Starting with the renderer, all we need to do is set web view client and web chrome client to our own web view client and chrome client.

 

public class HybridWebViewRenderer : WebViewRenderer
{
    public HybridWebViewRenderer(Context context) : base(context)
    { }
    public HybridWebViewRenderer(){ }

    protected override void OnElementChanged(ElementChangedEventArgs e)
    {
        base.OnElementChanged(e);

        Control.Settings.JavaScriptEnabled = true;
        var webView = Control as Android.Webkit.WebView;

        this.Control.SetWebViewClient(new HybridWebViewClient());
        this.Control.SetWebChromeClient(new HybridWebChromeClient());

        this.Control.LoadUrl(Control.Url);
    }
}

 

Then in web view client, basically what we gonna do is, find the particular button we want and then inject the event. The event is, of course, button click event and we gonna make it show up an alert. Yes, alert! Not just any alert, but an alert with specific keyword that we can recognize it later in web chrome client when we adding event listener.  For this example, I will use ‘dosomething’ and ‘dosomethingelse’ as our keywords. Let see how it looks like.

public class HybridWebViewClient : WebViewClient
{

    public override async void OnPageFinished(WebView view, string url)
    {
        base.OnPageFinished(view, url);

        int i = 10;
        while (view.ContentHeight == 0 && i-- > 0)
            await Task.Delay(1000);
        // find the particular button
        string funcurl = "var btn1 = document.getElementsByClassName('btn-primary')[1]; if(btn1 != null){btn1.addEventListener('click', function() { alert('dosomething'); }); }" +
                         "var btn2 = document.getElementsByClassName('btn-primary')[2]; if(btn2 != null){btn2.addEventListener('click', function() { alert('dosomethingelse'); }); }";
        view.LoadUrl("javascript: r(function(){" + funcurl + ");
        break;

    }
}

In web chrome client, we’ll filter any alert from the html page to find out if any of our alert has beed fired. We’ll override OnJsAlert method, do some process we suppose to do, and then cancel the result and  return it with true value. By doing that it means we won’t show the alert to user because it’s unnecessary for them to see our alert.

public class HybridWebChromeClient : WebChromeClient
{
    public override bool OnJsAlert(WebView view, string url, string message, JsResult result)
    {
        if(message.Contains("doseomething"))
        {
            // do something here
            result.Cancel();
            return true;
        }
        else if(message.Contains("dosomethingelse"))
        {
            // do something else here
            result.Cancel();
            return true;
        }
       return base.OnJsAlert(view, url, message, result);
    }
}

iOS Web View Renderer

In iOS, the process is slightly simpler, because we only need two classes, first the web view renderer class, and the of course the web view delegate class. Just like the Android project, all we need to do in renderer is just setting the delegate class to our own delegate class and the let the delegate class do all the job. This is how the renderer looks like.

public class HybridWebViewRenderer : WebViewRenderer
{
    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        Delegate = new HybridUiWebViewDelegate(this);

        this.ScrollView.DecelerationRate = UIScrollView.DecelerationRateNormal;
        var webView = e.NewElement as HybridWebView;

    }
}

Unlike Android, in iOS we don’t have web chrome client to catch javascript alert event, so we need to do something else. All we have is just a delegate class and all its override methods. But among those methods, there’s one method that will be very useful in situation like this. That method called ShouldStartLoad. This method is fired when user start navigating to new url and we can decide programmatically what the app gonna do when it happens. So, what we gonna do is, make the button load certain url, and then we catch the event in ShouldStartLoad method, do what we suppose to there and then cancel the request so web view won’t actually load our url. The url, of course, is a fake url that contain our keyword that we can identify, just like what we did with alert in Android.

public class HybridUiWebViewDelegate : UIWebViewDelegate
{
    HybridWebViewRenderer hybridWebViewRenderer;
    public HybridUiWebViewDelegate(HybridWebViewRenderer _webViewRenderer = null)
    {
        hybridWebViewRenderer = _webViewRenderer ?? new HybridWebViewRenderer();
    }
public override async void LoadingFinished(UIWebView webView)
    {
        var wv = hybridWebViewRenderer.Element as HybridWebView;
        if (wv != null)
        {
           await Task.Delay(100);// wait here till content is rendered

           // find the particular button
           string funcurl = string.Empty;
           funcurl = "var btn1 =  document.getElementsByClassName('btn-primary')[1]; if(btn1 != null){btn1.addEventListener('click', function() { window.location = \"dosomething\"; }); }" +
                     "var btn2 =  document.getElementsByClassName('btn-primary')[2]; if(btn2 != null){reject.addEventListener('click', function() { window.location = \"dosomethingelse\"; }); }";

           webView.EvaluateJavascript("javascript: r(function(){" + funcurl + "});");

    }

    public override bool ShouldStartLoad(UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType)
    {
        if(request.Url.ToString().Contains("dosomething"))
        {
            // do something here
            return false;
        }
        else if(request.Url.ToString().Contains("dosomethingelse"))
        {
            // do something else here
            return false;
       }

      return true;
    }
}

Credit:

Set Placeholder Color of Xamarin’s Picker

In one of my previous post, we’ve discussed about how to adjust font size of Xamarin’s Picker. Font size in picker, especially in Android picker, is simply fixed, so we need a workaround if we want to adjust it. In iOS, font size of the picker is more flexible, you can set height request and width request, or simply put it on a grid, the size of whole picker will follow its container. But there’s another property of picker that also fixed. It’s the placeholder color. I know, most of the time we won’t need to change the placeholder color of the picker. However, for the sake of the app’s theme, sometime we gonna need to set color of the placeholder. So, In case you need it, you can use this method to set the placeholder color.

Custom Picker

To set the placeholder color, first thing we gonna need, of course, a bindable property for it. So we gonna update our CustomPicker from previous post and add Placeholder Color property in it

 

public class CustomPicker : Picker
{
    public static readonly BindableProperty FontSizeProperty =
        BindableProperty.Create(nameof(FontSize), typeof(Int32), typeof(CustomPicker), 24, BindingMode.TwoWay);

    public Int32 FontSize
    {
        set { SetValue(FontSizeProperty, value); }
        get { return (Int32)GetValue(FontSizeProperty); }
    }

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

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

 

Custom Renderer Android

Next, we update the renderer in Android. In Android, representing the placeholder color, we have property called Hint Text Color. The only problem is Hint Text Color use Color class from Android.Graphic class unlike default Color of Xamarin from Xamarin.Form class. As a result, we need to parse the color. Fortunately, Android.Graphic.Color take hex code as its parameter so we can just simply parse it. The following code is how we do it.

public class CustomPickerRenderer : PickerRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
    {
        base.OnElementChanged(e);
        this.Control.Background = Forms.Context.GetDrawable(Resource.Drawable.CustomPickerBackground);
        Control?.SetPadding(20, 20, 20, 20);
        if (e.OldElement != null || e.NewElement != null)
        {
            var customPicker = e.NewElement as CustomPicker;
            Control.TextSize *= (customPicker.FontSize * 0.01f);
            Control.SetHintTextColor(Android.Graphics.Color.ParseColor(customPicker.PlaceholderColor));
        }
    }
}

Custom Renderer iOS

Nah, it’s where things got tricky. To set placeholder color, or in iOS term, Foreground Color, we gonna need class named UIColor. And UIColor doesn’t take Hex Code as its parameter. It takes RGB color. Consequently, we need to parse Hex to RGB first. I made three little functions to take the color Red, Green and Blue from our Hex Code.

Finish with the color thing, next we gonna put that color into something, called NSAttributedString. So, what is NSAttributedString? First, Placeholder Attribute in iOS Picker is a NSAttributedString, so we need to make our own NSAttributedString if we want to modify our placeholder. Second, NSAttributedString has many attributes that you’re able to modify. In this example we just use the Foreground Color. but it has many more important attributes such as Background Color , Font, and Stroke Color.

So, this is how the custom renderer in iOS looks like.

public class CustomPickerRenderer: PickerRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
    {
        base.OnElementChanged(e);
        if (e.OldElement != null || e.NewElement != null)
        {
            var customPicker = e.NewElement as CustomPicker;

            string placeholderColor = customPicker.PlaceholderColor;
            UIColor color = UIColor.FromRGB(GetRed(placeholderColor), GetGreen(placeholderColor), GetBlue(placeholderColor));

            var placeholderAttributes = new NSAttributedString(customPicker.Title,new UIStringAttributes()
            { ForegroundColor = color });

            Control.AttributedPlaceholder = placeholderAttributes;
        }
    }

    private float GetRed(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.R;
    }

    private float GetGreen(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.G;
    }

    private float GetBlue(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.B;
    }
}

Let’s try it out!

I made minor change to example I used in previous post. I add placeholder color and run it in Android and iOS emulator.

<ContentPage.Content>
    <StackLayout VerticalOptions="Center" Padding="30,0,30,0">
        <Label Text="Choose Your Origin!" TextColor="#9b59b6" FontSize="Large" />
        <control:CustomPicker Title="Select Country" PlaceholderColor="#4b86b4" />
        <control:CustomPicker Title="Select State" PlaceholderColor="#ffa500" />
        <control:CustomPicker Title="Select City" PlaceholderColor="#2ee0bd" />
    </StackLayout>
</ContentPage.Content>

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<CustomEditor>
{
    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.

<ContentPage.Content>
    <StackLayout Orientation="Vertical">
        <control:CustomEditor Placeholder="{Binding Placeholder}" Text="{Binding Text}" HeightRequest="100">
            <Editor.Behaviors>
                <local:EditorBehavior />
            </Editor.Behaviors>
        </control:CustomEditor>
        <Label Text="Welcome to Editor Page" HorizontalOptions="Center"/>
    </StackLayout>
</ContentPage.Content>

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.

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)