Rasmus Tolstrup Christensen

Image gallery with Xamarin Forms

March 21, 2016 | 6 Minute Read

Out of the box Xamarin Forms provides many standard cross platform controls. Recently I had the need to display images in an image gallery and among the many controls Xamarin Forms provides, they aren't exactly image galleries. So my solution was to build a cross platform image gallery.

We’ll create a reuseable control, capable of displaying images from an observablecollection. The control will support horizontal scrolling, and when a new photo is captured with the camera, it’s added to the gallery immediatly, using databinding. Further more we’ll take advantage of tap gestures and display a big version of an image, when it’s selected.

The source code is available here For a functional demo, watch the video at the bottom of this post or checkout the GitHub Repository.

imageGalleryLayout

The core of the image gallery is a standard StackLayout with images inside. The gallery itself will expose an ItemsSource property where we can databind an observable collection. Each time a photo is captured, the gallery will append the new photo. If we tap an image, it will be displayed in the preview area. Further more when the gallery becomes wider than the screen, horizontal scroll will be added. Time to checkout the code.

public class ImageGallery : ScrollView
{
    readonly StackLayout _imageStack;

    public ImageGallery ()
    {
        this.Orientation = ScrollOrientation.Horizontal;

        _imageStack = new StackLayout {
            Orientation = StackOrientation.Horizontal
        };

        this.Content = _imageStack;
    }

    public IList<View> Children {
        get {
            return _imageStack.Children;
        }
    }

We start by creating a class which inherits from ScrollView and set its orientation to Horizontal. Inside the ScrollView we make a StackLayout to contain all our Images, even though we define it as View in the code above. More on this later when we get to the ItemTemplate support.

ItemsSource support

We want to ensure that out image gallery supports databinding with MVVM and hence we want to expose a bindable property where the consumer of the control can attach a data bound collection. We do this by adding a BindableProperty ItemsSource.

public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create<ImageGallery, IList> (
        view => view.ItemsSource,
        default(IList), 
        BindingMode.TwoWay,
        propertyChanging: (bindableObject, oldValue, newValue) => {
            ((ImageGallery)bindableObject).ItemsSourceChanging ();
        },
        propertyChanged: (bindableObject, oldValue, newValue) => {
            ((ImageGallery)bindableObject).ItemsSourceChanged (bindableObject, oldValue, newValue);
        }
);

public IList ItemsSource {
    get {
        return (IList)GetValue (ItemsSourceProperty);
    }
    set {

        SetValue (ItemsSourceProperty, value);
    }
}

With this property we get access to ItemsSourceChanged and ItemsSourceChanging. The latter is not in scope here. ItemsSourceChanged provides access to values currently in the source and also added or removed items. In this post we only focus on items being added to the source, but it’s fairly easy to extend with remove.

var notifyCollection = newValue as INotifyCollectionChanged;
if (notifyCollection != null) {
    notifyCollection.CollectionChanged += (sender, args) => {
        if (args.NewItems != null) {
            foreach (var newItem in args.NewItems) {

                var view = (View)ItemTemplate.CreateContent ();
                var bindableObject = view as BindableObject;
                if (bindableObject != null)
                    bindableObject.BindingContext = newItem;
                _imageStack.Children.Add (view);
            }
        }
    };
}

We hook into CollectionChanged of our ItemsSource. For each new item added, we create an instance of the current ItemTemplate defined in XAML. The View we get from here, will be treated as a BindableObject itself, and from this point of view “untyped”. This means we can add almost any custom object to our collection and make the current defined ItemTemplate responsible for rendering.

As you can see from the source code, we can make usage of SelectedIndex but I’ve decided to make use of GestureRecognizers defined in the ItemTemplate when selecting an image to preview.

ItemTemplate for render and gesture support

The ItemTemplate we define when using our image gallery control is just a standard defined item template as you would also create if you use ListView etc.

<custom:ImageGallery.ItemTemplate>
    <DataTemplate>
        <Image
            Source="{Binding Source}"
            Aspect="AspectFit">
            <Image.GestureRecognizers>
                <TapGestureRecognizer
                    Command="{Binding Path=BindingContext.PreviewImageCommand, Source={x:Reference ThePage}}"
                    CommandParameter="{Binding ImageId}" />
            </Image.GestureRecognizers>
        </Image>
    </DataTemplate>
</custom:ImageGallery.ItemTemplate>

First we add an Image and bind it to the Source property of the items in the image gallery ItemsSource property, here implemented as an ImageSource. Next we define a TapGestureRecognizer, this one is a bit more tricky. Because we only have a single view and a single viewmodel in this sample, we make a Binding to a Command(PreviewImageCommand) on the MainViewModel, bound to the MainView aka the current BindingContext. Along the command we’ve also defined a CommandParameter which is a unique id of each image. This is used by the PreviewImageCommand, so select the image to preview.

With all this in place, all we need is a basic ViewModel with an ObservableCollection of images and a command to handle the preview gesture. You can find this in the GitHub Repository.

The final app will work like the video below, for both IOS and Android. appvideo  appimage

comments powered by Disqus