SQLite and ORM in Xamarin Forms

It’s about a week ago, I was looking for a complete tutorial about how to implement ORM in SQLite in Xamarin Forms. I did find out that there’s already a library called SQLiteNetExtensions that handle this kind of stuff. It’s also has complete documentation with it. Unfortunately, the documentation is not up to date. I was having difficulty to use that library at first, so I look up for other tutorials on the net. But I couldn’t find one that suit my need, a complete and up to date tutorial. This is actually pretty simple thing, but if you don’t know how to do it properly, it will cost you hours of your life before you can make it works. So, this is how I’ve done it.

Note : Nuget packages you need are SQLite.Net-PCL and SQLiteNetExtenstions

Database Strictures

First thing first. Because we’re talking about ORM, let’s have a simple data structure that represent all kind relationships in ORM; OneToMany, ManyToOne, ManyToMany and OneToOne.

public class Department
{
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }

    public string DepartmentName { get; set; }

    [OneToMany(CascadeOperations = CascadeOperation.All)]
    public List Employees { get; set; }
}

public class Employee
{
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }

    public string IDCardNumber { get; set; }
    public string EmployeeName { get; set; }

    [ForeignKey(typeof(Department))]
    public int DepartmentID { get; set; }

    [ManyToOne(CascadeOperations = CascadeOperation.All)]
    public Department Deparment { get; set; }

    [ForeignKey(typeof(EmployeeFamily))]
    public int FamilyID { get; set; }

    [OneToOne(CascadeOperations = CascadeOperation.All)]
    public EmployeeFamily Family { get; set; }

    [ManyToMany(typeof(EmployeeBenefit))]
    public List Benefits { get; set; }
}

public class EmployeeFamily
{
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }

    public string SpouseName { get; set; }
    public string FirstChildName { get; set; }
    public string SecondChildName { get; set; }

    [ForeignKey(typeof(Employee))]
    public int EmployeeID { get; set; }

    [OneToOne(CascadeOperations = CascadeOperation.All)]
    public Employee Employee { get; set; }
}

public class EmployeeBenefit
{
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }

    public string BenefitName { get; set; }
    public double BenefitValue { get; set; }

    [ManyToMany(typeof(Employee))]
    public List Employees { get; set; }
}

One thing to take a note is make sure you’re using SQLite and SqLiteExtensions.Attributes instead of SQL.Net.Attributes or it’s not gonna works.

Dependency Services

To implement sqlite to Android and iOS, of course we gonna need dependency service classes. But first let create the interface in our PCL project.

public interface ISQLite
{
    SQLite.SQLiteConnection GetSQLiteConnection();
}

Then we move on to Android project. This is how the dependency service class looks like.

public class SQLiteAndroid : ISQLite
{
    public SQLite.SQLiteConnection GetSQLiteConnection()
    {
        var fileName = "testing.db3";
        var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
        var path = Path.Combine(documentsPath, fileName);

        var platform = new SQLitePlatformAndroid();
        var connection = new SQLite.SQLiteConnection(path, true);

        return connection;
    }
}

Lastly, we also implement the dependency class to iOS project.

public class SQLiteiOS : ISQLite
{
    public SQLite.Net.SQLiteConnection GetConnection()
    {
        var sqliteFilename = "testing.db3";
        string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
        string libraryPath = Path.Combine(documentsPath, "..", "Library");
        var path = Path.Combine(libraryPath, sqliteFilename);
        var platform = new SQLitePlatformIOS();
        var conn = new SQLite.Net.SQLiteConnection(platform, path);
        return conn;
    }
}

Data Context

Next, let’s create a class that will handle all things SQLite related. So when we need to do some operations in SQLite, all we need to do just calling the method in this class. Below this is the sample code of the class, but I don’t include all operations possible, just some important operations for example.

public class EmployeeDataContext
{
    private static EmployeeDataContext instance;

    public static EmployeeDataContext Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new EmployeeDataContext();
            }
            return instance;
        }
    }

    private SQLite.SQLiteConnection connection;

    private EmployeeDataContext()
    {
        connection = DependencyService.Get().GetSQLiteConnection();
        CreateTableIfNotExist();
    }

    private void CreateTableIfNotExist()
    {

        bool isTableNotExist = false;

        try
        {
            var test = connection.Table().FirstOrDefault();
        }
        catch
        {
            isTableNotExist = true;
        }
if (isTableNotExist)
        {
            connection.CreateTable();
        }

    }

    public List GetAllDepartment()
    {
        return ReadOperations.GetAllWithChildren(connection, recursive: true).ToList();
    }

    public void RefreshDepartment(IList listDepartment)
    {
       WriteOperations.DeleteAll(connection, GetAllDepartment(), true);
       WriteOperations.InsertOrReplaceAllWithChildren(connection, listDepartment, true);
    }

    public void DeleteAllDepartment()
    {
        WriteOperations.DeleteAll(connection, GetAllDepartment(), true);
    }

    public void UpdateDepartment(Department department)
    {
        WriteOperations.UpdateWithChildren(connection, department);
    }

    public void UpdateEmployee(Employee employee)
    {
        WriteOperations.UpdateWithChildren(connection, employee);
    }
}

How to use it

Now, everything is ready, let’s see how to use it. You call the methods from view model or directly from code behind.


List listDepartment = new List();
Department department = new Department();
Employee employee = new Employee();

EmployeeDataContext.Instance.GetAllDepartment();
EmployeeDataContext.Instance.RefreshDepartment(listDepartment);
EmployeeDataContext.Instance.UpdateDepartment(department);
EmployeeDataContext.Instance.UpdateEmployee(employee);

Sample Code is available in my Github repo

 

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

 

Accordion ListView in Xamarin Forms

Accordion ListView or Expanded ListView, a list view that can show another list view inside its list view item, is one of UI components that Xamarin Forms doesn’t have it by default. So, in this post, I want to discuss one of the simplest way to create an accordion list view look alike in Xamarin Forms.

Custom Observable Collection

One property that we need the most to create accordion list view is visibility property with boolean data type. This property will determine wether the children list view is visible or not. So, when user tap to an item in list view, that property have to change. And because it’s only a property in one of the list view items, we gonna need custom observable collection that support that condition. Because regular observable collection won’t be support it. The property will change, but it won’t affect the UI view. I have discussed this custom observable collection in first post, and we gonna need it again in this case.


public class CustomObservableCollection : ObservableCollection
{
    public CustomObservableCollection() { }
    public CustomObservableCollection(IEnumerable items) : this()
    {
        foreach (var item in items)
           this.Add(item);
    }
public void ReportItemChange(T item)
    {
        NotifyCollectionChangedEventArgs args =
        new NotifyCollectionChangedEventArgs(
        NotifyCollectionChangedAction.Replace,
        item,
        item,
        IndexOf(item));
        OnCollectionChanged(args);
    }
}

<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;">&#65279;</span>

This custom observable collection add a method called ReportItemChange that will trigger CollectionEventChanged. So, when we change the visibility property, the UI view will be notified.

Entities

So, for example in this post, I will create list of countries which have list of cities on every country. So, these are entities that I use in this example.


public class City
{
    public string CityName { get; set; }
}

public class Country
{
    public string CountryName { get; set; }
    public bool IsChildrenVisible { get; set; }
    public string ArrowIconSource
    {
        get
        {
           if (IsChildrenVisible)
              return "uparrow.png";
           else
              return "downarrow.png";
        }
    }
    public ObservableCollection Cities { get; set; }
    public int ChildrenRowHeightRequest
    {
        get
        {
            if (Cities != null)
                return Cities.Count * 50;
            else
                return 0;
        }
    }
}

Country, as parent entity, has a visibility property called IsChildrenVisible that will determine whether the cities are showed or not. It also has ChildrenRowHeightRequest property to determine height request for Cities list view.

View Model

After entities, we move on to the view model. In view model we create list of countries that will be bind to the view. As a default, we will set the IsChildrenVisible property to false. And in this view model, we also have method called ShowCities that update that property.


public class AccordionCountriesViewModel : BaseViewModel
{
    private CustomObservableCollection countries;
    public CustomObservableCollection Countries
    {
       get => countries;
       set => SetProperty(ref countries, value);
    }

    public AccordionCountriesViewModel()
    {
       ObservableCollection USACities = new ObservableCollection()
       {
           new City(){ CityName = "Washington DC" },
           new City(){ CityName = "New York" },
           new City(){ CityName = "Los Angeles" }
       };

       ObservableCollection ChinaCities = new ObservableCollection()
       {
           new City(){ CityName = "Beijing" },
           new City(){ CityName = "Shanghai" },
           new City(){ CityName = "Shenzhen" }
       };

       ObservableCollection RussiaCities = new ObservableCollection()
       {
          new City(){ CityName = "Moscow" },
          new City(){ CityName = "St. Peterburg" },
          new City(){ CityName = "Kazan" }
       };

       countries = new CustomObservableCollection()
       {
          new Country(){ CountryName = "USA", IsChildrenVisible = false, Cities = USACities },
          new Country(){ CountryName = "China", IsChildrenVisible = false, Cities = ChinaCities },
          new Country(){ CountryName = "Russia", IsChildrenVisible = false, Cities = RussiaCities },
       };
   }

   public void ShowCities(Country mCountry)
   {
      Country country = Countries.SingleOrDefault(c => c.CountryName == mCountry.CountryName);
      country.IsChildrenVisible = !country.IsChildrenVisible;
      Countries.ReportItemChange(country);
   }
}

Let’s try it out

All preparations are complete. We will create a view which has list view inside another list view. And then we user tap the list view item, it will call method ShowCities in view model.

Screen Shot 2018-02-22 at 6.25.00 AM
And this is how we bind the view model and handle the item tapped event.


public partial class AccordionCountriesPage : ContentPage
{
    AccordionCountriesViewModel viewModel;
    public AccordionCountriesPage()
    {
         InitializeComponent();
         BindingContext = viewModel = new AccordionCountriesViewModel();
    }

    void Handle_ItemTapped(object sender, ItemTappedEventArgs e)
    {
        Country mCountry = (Country)e.Item;
        viewModel.ShowCities(mCountry);
    }
}

Now you can try and run the code.

Credit :

  • Arrow Icons from FlatIcon, Down Arrow icon by Freepik, Right Arrow icon by GraphicsBay, Up Arrow icon by Hadrien

 

UPDATE : Adding Animation to Accordion List View

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)

Sample Code is available in my Github repo

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


    
        
        
        
        
    

 

Sample Code is available in my Github repo.