Friday, August 28, 2015

Extending the ListView to make it clickable

The problem

The ListView is a fantastic control to use in Xamarin Forms. It has one big drawback, however and that is that if you navigate away from a selected item in the list and then navigate back, the same item is still selected and nothing happens when you tap it again.

The solution

We want to add a new command to the ListView and we start by adding a new class that inherits from the original ListView control. I usually create a folder called "Controls" in the Forms PCL and put stuff I want to use in the XAML in there.

   public class ListView : Xamarin.Forms.ListView
    {
        public static BindableProperty ItemClickCommandProperty = 
            BindableProperty.Create<ListView, ICommand>(x => x.ItemClickCommand, null);

        public ListView()
        {
            this.ItemTapped += this.OnItemTapped;
        }


        public ICommand ItemClickCommand
        {
            get { return (ICommand)this.GetValue(ItemClickCommandProperty); }
            set { this.SetValue(ItemClickCommandProperty, value); }
        }


        private void OnItemTapped(object sender, ItemTappedEventArgs e)
        {
            if (e.Item != null && this.ItemClickCommand != null && this.ItemClickCommand.CanExecute(e))
            {
                this.ItemClickCommand.Execute(e.Item);
            }

            this.SelectedItem = null;
        }
    }


The key here is the ItemClickCommand property. This will be the command that gets executed when we tap on an item. We also listen to the OnItemTapped event and the magic goes here by simply setting the SelectedItem to null again, allowing for the item to be tapped again.

Enter a ViewModel

Since MVVM is the way to go we need a view model. We are omitting the INotifyPropertyChanged stuff to keep it simple. What we have is an ObservableCollection of Duck objects that simple has a property called Name. We also define a command where we put the code that we want to execute when an item is tapped. In this case we navigate to a new page that simply displays the name of the duck. In the real world you should use IoC and also create a view model for the Duck view.

    // INotifyPropertyChanged implementation omitted - use fody!
    public class MainViewModel
    {
        public MainViewModel()
        {
            Ducks = new ObservableCollection<Duck>()
            {
                    new Duck() { Name = "Bob" },
                    new Duck() { Name = "Tommy" },
                    new Duck() { Name = "Donald" }
            };
        }

        public INavigation Navigation { get; set; }

        public ObservableCollection<Duck> Ducks
        {
            get;
            set;
        }

        public Command<Duck> DuckSelected
        {
            get
            {
                return new Command<Duck>(async (d) =>
                    {
                        var duckView = new DuckView();
                        duckView.LoadData(d);
                        await Navigation.PushAsync(duckView);
                    });
            }
        }

    }

    public class Duck
    {
        public string Name { get; set; }
    }

And the View 

This binds it all together. We assign the source of items from our view model as well as the command that we want to execute when an item is clicked.

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:local="clr-namespace:FancyList.Controls;assembly=FancyList"
             x:Class="FancyList.MainView" Padding="20">
    <ContentPage.Content>
        <local:ListView ItemsSource="{Binding Ducks}" ItemClickCommand="{Binding DuckSelected}">
            <ListView.ItemTemplate>
                <DataTemplate>
                      <ViewCell>
                        <ViewCell.View>
                              <Label Text="{Binding Name}" />
                          </ViewCell.View>
                      </ViewCell>
                </DataTemplate>
             </ListView.ItemTemplate>     
        </local:ListView>

    </ContentPage.Content>
</ContentPage>

Code behind

We also need to set the BindingContext in the code behind for the view. There are several navigation patterns out there and in this sample we take the easy path by simply passing in the Navigation object from the page (view) itself.

    public partial class MainView : ContentPage
    {
        public MainView()
        {
            InitializeComponent();

            var viewModel = new MainViewModel();
            viewModel.Navigation = this.Navigation;

            BindingContext = viewModel;
        }
    }

Summary

It's easy to extend controls in Xamarin Forms. This sample does however omit a lot of good practices like IoC and Fody. 

Resources

Tuesday, August 25, 2015

Using iOS standard icons in Xamarin Forms

Have you ever wished for using the standard iOS icons in the toolbar for Xamarin Forms applications? Then you are reading the correct blog post. If not, then you are in the wrong place.

The problem

We want to utilize the built in icons for iOS in our Xamarin Forms application to create something like the image below.



The solution

The good news is that is isn't really that complicated. Simply create a custom renderer and add some behavior to the NavigationRenderer.

First, the Xaml

The definition of our view looks like this.

xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    Title = "Welcome"
    x:Class="Test.MyPage">

    <ContentPage.ToolbarItems>
        <ToolbarItem Name="Add" />
        <ToolbarItem Name="Camera" />
    </ContentPage.ToolbarItems>

    <ContentPage.Content>
        <Label Text="Wow, that's cool!" HorizontalOptions="Center" VerticalOptions="Center" />
    </ContentPage.Content>

</ContentPage>


The key part here is to name your ToolbarItems to something useful and something that we can reference in the custom renderer. You also might want to make sure that the name works on Android and Windows Phone since those platforms won't be affected by this change.

Then the renderer

Being a swede, I have trouble saying the word Renderer... Rendererrerr... Anyhow, this is what it looks like in code. The key is to look at the title for each UIBarButtonItem and replace the entire button with a new one. So we first define a new list to hold our new buttons and then recreate them, one by one to finally assign the new list to the NavigationItem.

The renderer goes in the iOS project since it's iOS specific.

[assembly: ExportRenderer(typeof(NavigationPage), typeof(CustomNavigationRenderer))]

namespace ToolbarApp.iOS.Renderers
{
    public class CustomNavigationRenderer : NavigationRenderer
    {

        public override void PushViewController(UIKit.UIViewController viewController, bool animated)
        {
            base.PushViewController(viewController, animated);

            var list = new List<UIBarButtonItem>();

            foreach (var item in TopViewController.NavigationItem.RightBarButtonItems)
            {
                if(string.IsNullOrEmpty(item.Title))
                {
                    continue;
                }

                if (item.Title.ToLower() == "add")
                {
                    var newItem = new UIBarButtonItem(UIBarButtonSystemItem.Add)
                    {
                        Action = item.Action,
                        Target = item.Target
                    };
                    
                    list.Add(newItem);
                }

                if (item.Title.ToLower() == "camera")
                {
                    var newItem = new UIBarButtonItem(UIBarButtonSystemItem.Camera)
                        {
                            Action = item.Action,
                            Target = item.Target
                        };

                    list.Add(newItem);
                }

                TopViewController.NavigationItem.RightBarButtonItems = list.ToArray();
            }
        }
    }
}


Summary

It's pretty simple to utilize platform specific stuff in Forms and you should do that in order keep that native feeling. The key to success is Custom Renderers. Learn to use them and you'll excel in all parts of your life!

Resources

The original post from the Xamarin Forum. Thanks goes to Torben Kruse for initially answering this question.

A sample project for trying it out without coding a line.

Friday, August 21, 2015

Roboto Condensed Bold in Forms (iOS)

The last few days have been spent in pixel perfect land. I've done some fine tuning of a clients forms layouts that of course includes a bunch of custom fonts. I've started out with making sure that the iOS design is correct and we'll move on to Android and WP in another post perhaps.

This post is about a specific issue and that issue is how the naming of the font isn't all that matters.

Short version

Ttf-files only contain one weight. If you bold a regular version of a font it will be calculated bold and not the true bold since that one is contained in another file. To get the real bold, you need to reference the ttf-file containing the bold weight.

Start off here for the longer version

To get a grip of how to use custom fonts, please check out the Xamarin documentation at http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/fonts/ and also a post from Mike James that can be found here: https://blog.xamarin.com/custom-fonts-in-ios/

So what's the problem?

To summarize the usage of custom fonts loaded from a ttf-file on iOS, all described in the links above.

  • Add the ttf-file to the Resource folder - make it a Bundled Resource and Copy Always
  • Edit the info.plist to include the font
And then to use the font we do something like

<Label FontFamily="Roboto Condensed" Text="Very custom" />

To find out the FontFamily name to use, you open up the ttf-file in Windows by simply double clicking it and you'll find it. Since my client is a client, they wanted to use a different typeface for the bold text. No problem, I shouted and downloaded the bolder typeface perfectly named Roboto Condensed Bold.

I then opened the ttf file in windows to check out the name and it was named exactly the same. Wait what? How do I then distinguish between them?

It turns out that the Bold font has the same name and will be used if you set FontAttributes="Bold".

<Label FontFamily="Roboto Condensed" Text="Very custom" FontAttributes="Bold" />

And if you use the above without importing the "Roboto Condensed Bold" font it will Bold the regular font.

Still, what's the problem?

Really, nothing when I come to think of it. TrueType only supports one weight per file from what I understand and that's what I've missed in the past. I just thought that a font is a font and all is included within. :)

Also, if you only supply the regular weight and bold it, it would be some kind of calculated boldness instead of the real bold font that is meant to be used.

Extra material

Instead of using the FontAttributes that only supports Bold, Normal and Italic you can specify what font you'd like to us in the Font-attribute. For example, If I want to use Roboto Condensed Light I must specify the font name like this:

<Label FontFamily="Roboto Condensed" Text="Very custom" Font="RobotoCondensed-Light" />


How would I know what font names that are available for a font family? I use the following line of code on iOS to find out. 

var f =  UIFont.FontNamesForFamilyName("Roboto Condensed");

Summary

Make sure you reference all the weights you want and if they have the same name you only have to change the FontAttributes to select the font you wish for. But be careful and check that you actually get the weight you want since it would still bold a regular font instead of picking the correct one if you miss something along the way.

Resources

TrueType on Wikipedia - https://en.wikipedia.org/wiki/TrueType

Wednesday, August 19, 2015

Different images for selected and unselected using Xamarin Forms Tab Bar

This is just a simple hack for iOS using Xamarin Forms and icons for a TabbedPage.

Problem definition

In Xamarin Forms, the icon of the tab bar is fetched from what ever image you reference in the Icon property of the page added to the TabbedPage. This image is grayed out if unselected and filled in with the defined color when selected.
However, sometimes you want to display a different image if unselected or selected and there is no build in way to specify this nicely.

Solution

The solution is a small hack. Simply add event handlers to the Appearing and Disappearing events and reset the images to your liking.

         var activeImage = "active";
            var inactiveImage = "inactive";

            var tab1 = new AnotherPage();
            tab1.Title = "Check in";
            tab1.Icon = new FileImageSource() { File = activeImage };
            tab1.Appearing += (s, a) => tab1.Icon = new FileImageSource() { File = activeImage };
            tab1.Disappearing += (s, a) => tab1.Icon = new FileImageSource() { File = inactiveImage };



Summary

Should it be done? Probably not, but if your out of options (that is your customer insist of doing it in such way) then this is an option.

Also worth noting is that the images will flicker if the debugger is attached. There is no visible delay when running it in release.