Add Custom Back Button on Android

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

Accessing Toolbar from Page Renderer

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

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

Customize Android Logo

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

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

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

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

Navigation Handling

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

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

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

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

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

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

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

 

Credit:

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

Bindable Check Box and Radio Button

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

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

Custom Content View

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


	
         
            
                
                
            

            
            
        
	

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

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

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

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

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

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

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

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


	
        
            
                
                
            

            
            
        
	

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

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

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

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

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

View Model

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

public class BindableRadioButtonViewModel : BaseViewModel
{
    protected bool isSelected;
    public bool IsSelected
    {
        get => isSelected;
        set
        {
            SetProperty(ref isSelected, value);
           UpdateIconSource();
        }
    }
    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 bool enableProperty;
    public bool EnableProperty
    {
        get => enableProperty;
        set => SetProperty(ref enableProperty, value);
    }

    protected void UpdateIconSource()
    {
        if (IsSelected)
            IconSource = "RadioButtonChecked.png";
        else
            IconSource = "RadioButtonUnchecked.png";
    }

}
public class BindableCheckBoxViewModel : BaseViewModel
{
    protected bool isEnabled;
    public bool IsEnabled
    {
        get => isEnabled;
        set
        {
            SetProperty(ref isEnabled, value);
            UpdateImageSource();
            UpdateTitleColor();
        }
    }

    protected bool isChecked;
    public bool IsChecked
    {
        get => isChecked;
        set
        {
            SetProperty(ref isChecked, value);
            UpdateImageSource();
            UpdateTitleColor();
        }
    }

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

    protected Color titleColor;
    public Color TitleColor
    {
        get => titleColor;
        set => SetProperty(ref titleColor, value);
    }

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

    public Color DefaultTitleColor { get; set; }

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

    protected void UpdateTitleColor()
    {
        if(IsEnabled)
        {
            if(IsChecked)
            {
                TitleColor = DefaultTitleColor;
            }
            else
            {
                TitleColor = Color.Black;
            }
        }
        else
        {
            TitleColor = Color.FromHex("#b7b7b7");
        }
    }

    public void SetEnable(bool _isEnable)
    {
        IsEnabled = _isEnable;
    }
}

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

Implementation

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


	
        
            
                
                
            

             
                    
                        
                            
                        
                    
             

            
               
                  
                      
                  
               
            
        
	

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

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

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

    public ICommand RadioButtonTappedCommand { get; set; }

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

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

    public ICommand CheckBoxTappedCommand { get; set; }

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

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

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

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

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

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

Credit:

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

Changing Navigation Bar Color Dynamically

So, I’m still working on my current project that I discussed in my last two posts. And recently new requirement came to my desk. Our UX designer just created a wonderful mock up, which I was really sure would impress our client, but unfortunately I didn’t know how to implement it to real app. I wasn’t even sure it was possible. She made something like thematic menu, so she picked different color on everything, including the navigation bar, depended on the menu user selected. And it took me all day long searching on internet and going through countless trial and errors before figuring out the solution.

Before I continue, I want to give appreciation to this amazing blog post, Let’s Override Navigation Bar Button Click in Xamarin Forms. This post has been enormous help for me finding solution to this problem. I really recommend any of you to visit that blog because you will find many great useful post there. Now, let’s back to the business.

Navigation View Model

So, when I was doing a research about Navigation Bar in Xamarin forms, I found out that some properties in Navigation Class could be bind, one of the them is BarBackgroundColor. Other bindable properties that could be useful are BarTextColor and BarBackgroundImage. Well, I believe you can guess the function of that properties based on its name, but for this example I will just discuss about BarBackgroundColor, a property that determines the color of the navigation bar. So, to bind a color to that property, I created a view model, and then bind that view model to the Navigation Page. Here is  code for the view model.

public class NavigationViewModel : BaseViewModel
{
    Color barBackgroundColor = Color.Transparent;
    public Color BarBackgroundColor
    {
        get => barBackgroundColor;
        set => SetProperty(ref barBackgroundColor, value);
    }
}

Binding BarBackgroundColor

Using breakfast example from my two last post, let’s go back to the Breakfast View Model. In this class ,I updated (again), about how my app will navigate. And just before the current page navigate to other page, I will bind the navigation bar color. Here’s the latest Breakfast View Model looks like. Note: (again) I only attach the changed parts.

public BreakfastViewModel()
{
    BreakfastMenuList = new ObservableCollection<BreakfastMenu>()
    {
        new BreakfastMenu() { ImageSource = "Burger.png", MenuTitle = "BURGER", BackgroundColor="#50616e" },
        new BreakfastMenu() { ImageSource = "Pizza.png", MenuTitle = "PIZZA", BackgroundColor="#093f89" },
        new BreakfastMenu() { ImageSource = "EggBacon.png", MenuTitle = "BACON", BackgroundColor="#00a5ec" },
        new BreakfastMenu() { ImageSource = "Sandwich.png", MenuTitle = "SANDWICH", BackgroundColor="#37248a" },
    };
    MenuTappedCommand = new Command( () => MenuSelected());
}

private void MenuSelected()
{
    NavigationPage navPage = new NavigationPage();
    NavigationViewModel navViewModel = new NavigationViewModel();

    switch (SelectedBreakfastMenu.MenuTitle)
    {
        case "BURGER":
            navPage = new NavigationPage(new BreakfastBurger());
            break;
        case "PIZZA":
            navPage = new NavigationPage(new BreakfastPizza());
            break;
        case "BACON":
            navPage = new NavigationPage(new BreakfastBacon());
            break;
        case "SANDWICH":
            navPage = new NavigationPage(new BreakfastSandwich());
            break;
    }

    navViewModel.BarBackgroundColor = Color.FromHex(SelectedBreakfastMenu.BackgroundColor);
    navPage.BindingContext = navViewModel;
    navPage.SetBinding(NavigationPage.BarBackgroundColorProperty, new Binding("BarBackgroundColor"));
    Application.Current.MainPage = navPage;
}

As you can see at the code above, We don’t need any parameter anymore from the page, because I was’t actually navigating. I just switched out the current main page with the new one. Because if I had used PushAsync just like in my previous post, I would’ve had double navigation bars, which is unwanted. But this method allow me to change navigation bar color in each menu without having additional nav bar. But it also has obvious side effect. Yes, whenever user try to navigate to the previous page, it will directly navigate to the outside of the app, because it doesn’t have any previous page in navigation stack. Nah, it’s where the blog post that I mention earlier, come in handy. We will override the navigation process.

Handling Back Navigation

I will left the detail explanation to the original source. That blog post contains everything you need to know about overriding back button. I will just show you, how I implement this awesome method to my project. I just inherit this CoolContentPage class and add this few lines to my destination page, and everything works like wonder.

public partial class BreakfastPizza : CoolContentPage
{
    public BreakfastPizza()
    {
        InitializeComponent();

        if (EnableBackButtonOverride)
        {
            this.CustomBackButtonAction = () =>
            {
                Application.Current.MainPage = new BreakfastPage();
             };
        }
     }
}

UPDATE : Add Custom Back Button on Android

Credit:

  • Icon from FlatIcon, Burger icon by Freepik, Pizza icon by Smashicons, EggBacon and Sandwich Icon by Twitter
  • Override Navigation Back Button by Udara Alwis (blog)

PushAsync from View Model

In my last post, we discussed about creating grid menu with help of Flow List View and how I navigated to other page from view model using Push Modal Async. Well, later I found out that method had one crucial weakness. When I tried it out in my iPhone simulator, after I chose one of the menus, I couldn’t go back to my previous page because in iPhone we don’t have back button. I did a further research on internet and came out with a solution.

Change the root page into navigation page

When I did the research, there was one keyword that always appeared, whether in Stack Overflow or Xamarin Forum, it was changing the root page into navigation page. It sound simple but it took me a while to apply it properly. And after some trial and errors, here’s what I finally did to my view model. Note: I only attach the updated parts and commented the original source to distinguish the different.

 

//private INavigation navigation;
private BreakfastPage breakfastPage;

public BreakfastViewModel(BreakfastPage breakfastPage)
{
  //this.navigation = navigation;
  this.breakfastPage = breakfastPage;
  BreakfastMenuList = new ObservableCollection<BreakfastMenu>()
  {
    new BreakfastMenu() { ImageSource = "Burger.png", MenuTitle = "BURGER" },
    new BreakfastMenu() { ImageSource = "Pizza.png", MenuTitle = "PIZZA" },
    new BreakfastMenu() { ImageSource = "EggBacon.png", MenuTitle = "BACON" },
    new BreakfastMenu() { ImageSource = "Sandwich.png", MenuTitle = "SANDWICH" },
  };
  MenuTappedCommand = new Command(async () => await MenuSelectedAsync());
}

private async Task MenuSelectedAsync()
{
  Application.Current.MainPage = new NavigationPage(breakfastPage);
  switch (SelectedBreakfastMenu.MenuTitle)
  {
    case "BURGER":
        await Application.Current.MainPage.Navigation.PushAsync(new BreakfastBurger());
        //await navigation.PushModalAsync(new BreakfastBurger());
        break;
    case "PIZZA":
        await Application.Current.MainPage.Navigation.PushAsync(new BreakfastPizza());
        //await navigation.PushModalAsync(new BreakfastPizza());
        break;
    case "BACON":
        await Application.Current.MainPage.Navigation.PushAsync(new BreakfastBacon());
        //await navigation.PushModalAsync(new BreakfastBacon());
        break;
    case "SANDWICH":
        await Application.Current.MainPage.Navigation.PushAsync(new BreakfastSandwich());
       //await navigation.PushModalAsync(new BreakfastSandwich());
       break;
  }

}

As you can see, I didn’t use INavigation anymore. Instead, I passed the content page through view model’s constructor then turn it into navigation page. Next, I set the Application’s current main page into my new navigation page and that enable me to call push async from view model. The following code is how I bind the view model to content page.

public partial class BreakfastPage : ContentPage
{
  public BreakfastPage()
  {
    InitializeComponent();
    BindingContext = new BreakfastViewModel(this);
  }
}

Hide Navigation Bar

If there’s one thing that I didn’t like about this method is the navigation bar. Turning my root page into navigation page causing it to have navigation bar, which I didn’t need. So, I also updated the xaml file of my menu by adding one line to hide navigation bar in content page.

<ContentPage NavigationPage.HasNavigationBar="False" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:flv="clr-namespace:DLToolkit.Forms.Controls;assembly=DLToolkit.Forms.Controls.FlowListView" x:Class="Testing.Views.BreakfastPage">
  <ContentPage.Content ...>
</ContentPage>

Credit:

  • Icon from FlatIcon, Burger icon by Freepik, Pizza icon by Smashicons, EggBacon and Sandwich Icon by Twitter

Grid Menu with Flow List View

I’m currently working in a project that require me to create a grid menu. In my previous project, I manually created it using grid, but later I found out a more effective way to create grid menu. I’m using third party library, created by the infamous Daniel Luberda, called Flow List View. Unlike the default list view which only has one column, flow list view allow you to set how many columns you want for your list view. In this post I want to discuss about how basically we can utilize this flow list view with MVVM pattern.

Selected Item Handling

When you create a menu for your app, there are, at least, two things you have to handle. First, how it handles user input and second how to navigate to other pages. To handle user input, you can see the following xaml code to get glimpse how it’ll be done.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:flv="clr-namespace:DLToolkit.Forms.Controls;assembly=DLToolkit.Forms.Controls.FlowListView"              x:Class="App1.Pages.Breakfast.BreakfastPage">
 <ContentPage.Content>
  <Grid VerticalOptions="Fill" HorizontalOptions="Fill">
   <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="*"/>
   </Grid.RowDefinitions>

  <ContentView Grid.Row="0" HeightRequest="100" Padding="20">
   <Label Text="What do you want for breakfast?" FontAttributes="Bold" HorizontalOptions="Center" VerticalOptions="Center" TextColor="DarkBlue" FontSize="Large"/>
  </ContentView>

 <ScrollView Grid.Row="1">
  <flv:FlowListView FlowItemsSource="{Binding BreakfastMenuList}" FlowLastTappedItem = "{Binding SelectedBreakfastMenu}" FlowItemTappedCommand ="{Binding MenuTappedCommand}" FlowColumnCount="2" SeparatorVisibility="None" Margin="30,0,30,0" HasUnevenRows="True">
   <flv:FlowListView.FlowColumnTemplate>
    <DataTemplate>
     <Frame Margin="15,10,15,10" CornerRadius="5" HeightRequest="120" >
      <Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Margin="-20">
       <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
       </Grid.RowDefinitions>

      <Image Grid.Row="1" Source="{Binding ImageSource}" HorizontalOptions="Center" VerticalOptions="Center"/>
      <ContentView Grid.Row="2" BackgroundColor="DarkBlue" HeightRequest="40">
      <Label Text="{Binding MenuTitle}" TextColor="White" FontSize="Medium" FontAttributes="Bold" HorizontalOptions="Center" VerticalOptions="Center"/>
      </ContentView>
      </Grid>
     </Frame>
    </DataTemplate>
   </flv:FlowListView.FlowColumnTemplate>
  </flv:FlowListView>
 </ScrollView>

  </Grid>
 </ContentPage.Content>
</ContentPage>

Take a look once again at Flow List View. As you can see it has several properties, two of them are Flow Last Tapped Item and Flow Item Tapped Command. Base on it’s name, you can guess the function of those properties. You can bind the menu item that user just tapped to Flow Last Tapped Item, and then Flow Item Tapped Command will execute the command to handle the tapped event. So, when user tap one of the menus, we can record what menu he just tapped and do action accordingly. Another important property is Flow Column Count, where you set how many column your menu will be.

Navigation Handling

After we’ve done doing binding in xaml file, we move on to the view model. In view model, to navigate to another page, we’ll need INavigation from the class binder. We’ll pass it from constructor and use it to navigate to other page depend on the tapped menu. The following codes are the example how I did it.

 

public class BreakfastViewModel : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  private INavigation navigation;
  private ObservableCollection<BreakfastMenu> breakfastMenuList;
  private BreakfastMenu selectedBreakfastMenu;
  public ObservableCollection<BreakfastMenu> BreakfastMenuList
  {
   get => breakfastMenuList;
   set => SetObservableProperty(ref breakfastMenuList, value);
  }
  public BreakfastMenu SelectedBreakfastMenu
  {
   get => selectedBreakfastMenu;
   set => SetObservableProperty(ref selectedBreakfastMenu, value);
  }

  public ICommand MenuTappedCommand { get; set; }

  public BreakfastViewModel(INavigation navigation)
  {
   this.navigation = navigation;
   BreakfastMenuList = new ObservableCollection<BreakfastMenu>()
   {
     new BreakfastMenu() { ImageSource = "Burger.png", MenuTitle = "BURGER" },
     new BreakfastMenu() { ImageSource = "Pizza.png", MenuTitle = "PIZZA" },
     new BreakfastMenu() { ImageSource = "EggBacon.png", MenuTitle = "BACON" },
     new BreakfastMenu() { ImageSource = "Sandwich.png", MenuTitle = "SANDWICH" },
   };
   MenuTappedCommand = new Command(async () => await MenuSelectedAsync());
 }

private async Task MenuSelectedAsync()
{
 switch (SelectedBreakfastMenu.MenuTitle)
 {
  case "BURGER":
   await navigation.PushModalAsync(new BreakfastBurger());
   break;
  case "PIZZA":
   await navigation.PushModalAsync(new BreakfastPizza());
   break;
  case "BACON":
   await navigation.PushModalAsync(new BreakfastBacon());
   break;
  case "SANDWICH":
   await navigation.PushModalAsync(new BreakfastSandwich());
   break;
  }

}

protected void SetObservableProperty<T>(ref T field, T value,
[CallerMemberName] string propertyName = "")
{
  if (EqualityComparer<T>.Default.Equals(field, value)) return;
  field = value;
  OnPropertyChanged(propertyName);
}

protected virtual void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

 

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class BreakfastPage : ContentPage
{
 public BreakfastPage()
 {
   InitializeComponent();
   BindingContext = new BreakfastViewModel(Navigation);
 }
}

If there’s anything you need to pay more attention is, how I navigate. I used PushModalAsync instead of, the more commonly used, PushAsync, because PushAsync is not supported globally to be used in view model.

Credit:

  • Icon from FlatIcon, Burger icon by Freepik, Pizza icon by Smashicons, EggBacon and Sandwich Icon by Twitter
  • Flow List View by Daniel Luberda (github)

Simple Way to bind Selected Item Changed to View Model

As we know together, in Xamarin we can bind command and command parameter to a button. That command will be fired when button is clicked and we can pass parameter through command parameter. But it’s kinda more complicated when it comes to picker or list view. We have selected index changed and item selected in picker and list view respectively to handle the event when user pick a certain item, but we need function in back-end to capture the event before we process it to view model. We also can use behavior to bind the event directly to view model, but it’s bit too complicated for me, especially to do simple task like this. And recently I found a simple workaround to address this problem.

Property Binding

What I do is binding the Selected Item property to certain variable in view model. The binding mode can be one way to source, or two way if you want to initialize the selected item. Continuing from my previous post, I will use picker instead of list view in this example. I will also use the custom picker that I used in previous post. But the trick will also works for list view.

I’ve created two pickers in xaml file and the value that bind to the second picker will be depended to what user choose in first picker.   Here’s how the code look like.

<?xml version="1.0" encoding="utf-8" ?>
 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:control="clr-namespace:App1.CustomControls" x:Class="App1.Pages.PizzaPage">
  <ContentPage.Content>
   <StackLayout VerticalOptions="Center" Padding="30,0,30,0">
    <Label Text="Choose your Pizza!!!"  TextColor="#118E8C" FontSize="Large"/>
    <control:CustomPicker Title="Select Pizza Country" ItemsSource="{Binding CountryList}" FontSize="48" SelectedItem="{Binding SelectedCountry, Mode=TwoWay}"/>
    <control:CustomPicker Title="Select Pizza Type" ItemsSource="{Binding PizzaList}" FontSize="42"/>
   </StackLayout>
  </ContentPage.Content>
 </ContentPage>

Selected Item Handling

As you can see, in the first picker, Selected Item property is bind to property named Selected Country. In the view model, when this variable is set, I will call function to update the list that is bind to the second picker. So, whenever user change his choice in first picker, the value of second picker will also change simultaneously. The following code is how the view model look like.

public class PizzaViewModel : INotifyPropertyChanged
{
  private ObservableCollection<string> countryList;
  private ObservableCollection<string> pizzaList;
  private string selectedCountry;

  public ObservableCollection<string> CountryList
  {
    get => countryList;
    set
    {
      SetObservableProperty(ref countryList, value);
    }
  }
  public ObservableCollection<string> PizzaList
  {
   get => pizzaList;
   set => SetObservableProperty(ref pizzaList, value);
  }

  public string SelectedCountry
  {
    get => selectedCountry;
    set
    {
      if (value != selectedCountry && value != null)
      LoadPizzaType(value);
      SetObservableProperty(ref selectedCountry, value);
    }
}

public event PropertyChangedEventHandler PropertyChanged;

public PizzaViewModel()
{
  LoadPizzaCountry();
}

private async void LoadPizzaCountry()
{
  CountryList = await PizzaServices.GetPizzaCountry();
}

private async void LoadPizzaType(string country)
{
  PizzaList = await PizzaServices.GetPizzaType(country);
}

protected void SetObservableProperty<T>(ref T field, T value,
[CallerMemberName] string propertyName = "")
{
  if (EqualityComparer<T>.Default.Equals(field, value)) return;
  field = value;
  OnPropertyChanged(propertyName);
}

protected virtual void OnPropertyChanged(string propertyName)
 => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Take a look at variable Selected Country, it’s where the trick lies. With this simple trick, you can bind something similar like command and command parameter in one packet to picker and list view.

Adjusting font size of Xamarin’s Picker

When you’re developing the front-end side of a Xamarin’s app, you’re demanded to put and arrange every UI component in the right place, with right size and right color.  When it comes to size, it’s kinda troublesome to adjust picker to precise font size. The font size of picker in Xamarin, including DatePicker and TimePicker, is simply fixed. So, in this post, we will discuss about a workaround method to approach this problem. With this method, you will be even able to bind the font size of the picker to certain variable in view model if you want to. Note: so far, it works in Android, I still haven’t tried it in iOS. I’ll update this post or make another post after try it in iOS.

Custom Picker Class

The first step is we will create the custom class of the picker. This custom class will inherit picker class and we’ll add one binding property, font size, to this class. The following code is the example of the custom class.


 public class CustomPicker : Picker
    {
        public static readonly BindableProperty FontSizeProperty =
            BindableProperty.Create(nameof(FontSize), typeof(Int32), typeof(CustomPicker),
                10, BindingMode.Default);
        public Int32 FontSize
        {
            get => (Int32)GetValue(FontSizeProperty);
            set => SetValue(FontSizeProperty, value);
        }
    }

Updating Picker UI

The second step, we move on to the Android project. This part is just about the UI, nothing to do with the functionality of the picker. You can ignore and jump to next step if you feel don’t need it. So, in this part, I made and axml file which define the the background of the picker. As you know, standard Xamarin is just a text with underline, not so eye catching. So, I make little bit modification on it. I put the file in Drawable folder inside Resources.


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_enabled="false" >
    <shape android:shape="rectangle">
      <solid android:color="#EAEAEC"/>
      <corners android:radius="0dp" />
      <stroke android:width="1dp" android:color="#118E8C" />
    </shape>
  </item>

  <item>
    <shape android:shape="rectangle">
      <solid android:color="#80ffffff"/>
      <corners android:radius="1dp" />
      <stroke android:width="1dp" android:color="#118E8C" />
    </shape>
  </item>

</selector>

Custom Picker Renderer

The third step is the most important part. We gonna make custom renderer of the picker. Inheriting from PickerRenderer class, we will adjust the text size property of the picker base on font size property that we’ve created earlier. I use 0.01 as the multiplier just because it suit my need in my previous project, you can change it to meet your need if you feel need to.


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

        }
    }

Implementation

Now let’s try it out. The following codes if the sample of xaml file where I use the custom picker, followed by the view model.


<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:control="clr-namespace:App1.CustomControls" x:Class="App1.Pages.PickerPage">
    <ContentPage.Content>
        <StackLayout VerticalOptions="Center" Padding="30,0,30,0">
            <Label Text="Choose Your Origin!" TextColor="#118E8C" FontSize="Large"/>
            <control:CustomPicker Title="Select Country" ItemsSource="{Binding CountryList}" FontSize="48"/>
            <control:CustomPicker Title="Select State" ItemsSource="{Binding StateList}" FontSize="42"/>
            <control:CustomPicker Title="Select City" ItemsSource="{Binding CityList}" FontSize="36"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

 public class PickerViewModel : INotifyPropertyChanged
    {

        private List<string> countryList;
        private List<string> stateList;
        private List<string> cityList;

        public List<string> CountryList
        {
            get => countryList;
            set => SetObservableProperty(ref countryList, value);
        }

        public List<string> StateList
        {
            get => stateList;
            set => SetObservableProperty(ref stateList, value);
        }

        public List<string> CityList
        {
            get => cityList;
            set => SetObservableProperty(ref cityList, value);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public PickerViewModel()
        {
            CountryList = new List<string>() { "USA", "China", "Russia" };
            StateList = new List<string>() { "California", "Sichuan", "Kirov" };
            CityList = new List<string>() { "Los Angeles", "Guangzhou", "St. Petersburg" };
        }

        protected void SetObservableProperty<T>(ref T field, T value,
            [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) return;
            field = value;
            OnPropertyChanged(propertyName);
        }

        protected virtual void OnPropertyChanged(string propertyName)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PickerPage : ContentPage
    {
        public PickerPage()
        {
            InitializeComponent();
            BindingContext = new PickerViewModel();
        }
    }

When you run the project, you will have three different picker with three different size. You can try to bind the font size property with any integer variable you have within your view model.

 

Update : Set Placeholder Color of Xamarin’s Picker