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.


 
  
   
    
    
   

  
   
  

 
  
   
    
     
      
       
        
        
       

      
      
      
      
      
     
    
   
  
 

  
 

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 breakfastMenuList;
  private BreakfastMenu selectedBreakfastMenu;
  public ObservableCollection 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()
   {
     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(ref T field, T value,
[CallerMemberName] string propertyName = "")
{
  if (EqualityComparer.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.