Adding Tapped Event Gestures to Any XAML Control with Xamarin Forms

By in ,
No comments

We previously looked at how to create User Controls with Xamarin Forms to display an Image Button for the different activities in our Falafel 2 Go application. Now that we have these reusable buttons, we need to make them tab-enabled so that they can launch the appropriate activity. As we’ll see in today’s post, this is very simple to implement with Xamarin Forms.

Native Tap Events

By default, only a small selection of Xamarin Forms controls expose events such as “Tapped” or “Selected” natively. ListView and the related ViewCell are examples, which makes sense as that is generally the purpose of such controls. We used the ListView in our early experimentation with designing layouts for Falafel 2 Go, which allowed us to make convenient use of the ItemTapped event to launch the activity, as shown in this code sample:

<ListView x:Name="list" ItemsSource="{Binding Activities}" ItemTapped="OnActivitySelected">

                <ListView.ItemTemplate>

                    <DataTemplate>

                        <ViewCell>

                            <ViewCell.View>

                                <StackLayout Orientation="Vertical">

                                    <Label Text="{Binding Title}" Font="{x:Static common:FontResources.Header}" />

                                </StackLayout>

                            </ViewCell.View>

                        </ViewCell>

                    </DataTemplate>

                </ListView.ItemTemplate>

            </ListView>

We can then detect the selected Activity and navigate to the appropriate view, as shown in the code behind sample here:

public void OnActivitySelected (object o, ItemTappedEventArgs e)

        {

            var activity = e.Item as Activity;

            ContentPage page = activity.GetContentPage ();

            Navigation.PushAsync (page);

        }

However, as we saw in the last post, we replaced the simpler ListView with a custom control, which does not expose such a Tapped property…

Gesture Recognizers

Fortunately, Xamarin Forms also includes a Gesture Recognizer framework for detecting and handling physical interactions with the UI through the GestureRecognizer class. Currently, the only supported gesture is TapGestureRecognizer but it turns out this is completely sufficient for our needs. Each UI element in Xamarin Forms includes the property GestureRecognizers which is a collection which can contain any and all gestures that the element should handle. Here’s a quick sample from the docs that shows the nuts and bolts of how to create such a handler event:

var tapGestureRecognizer = new TapGestureRecognizer();

tapGestureRecognizer.Tapped += (s, e) => {

    // handle the tap

};

image.GestureRecognizers.Add(tapGestureRecognizer);

  Now, because this button is going to be reused, pointing to different Activity instances, we can’t hard-code the specific tap behavior in the button itself. Instead, we expose an EventHandler to which the ActivitiesPage can subscribe, and if present, call it from the Tapped EventHandler. This allows us hook up an external method to this behavior so that it can be launched from the main Activities view, which we’ll see shortly. Originally, we created and attached the handler to the entire StackLayout button itself, which would make sense since the entire control itself is the button. However, this proved not to be what we wanted, as it also made the blank areas around the icon and text register taps, which made it easy to accidentally launch an activity. Instead, we copied the same handler into both the Image and Label controls so that only these specific parts of the control would accept gestures. Here’s the full code-behind for the StackLayout button showing how the handlers are attached.

public StackLayoutButton()

        {

            InitializeComponent();

            var tgr = new TapGestureRecognizer();

            tgr.Tapped += (s, e) =>

            {

                if (Tapped != null) this.Tapped(s, e);

            };

            this.Icon.GestureRecognizers.Add(tgr);

            this.Text.GestureRecognizers.Add(tgr);

        }

  As I mentioned before, we can now attach the handler directly in our XAML, as shown in this short snippet for the Blog StackLayoutButton.

<controls:StackLayoutButton BindingContext="{Binding Blog}" Tapped="OnButtonTapped" />

Notice we are binding both the Activity and the new Tapped Event Handler we created to allow each button to launch its own bound activity. Here’s the code for the Tapped event showing how this is done:

protected void OnButtonTapped(object sender, EventArgs e)

        {

            var container = sender as View;

            if (container == null) return;

            var activity = container.BindingContext as Activity;

            if (activity == null) return;

            ContentPage page = activity.GetContentPage();

            Navigation.PushAsync(page);

        }

  With that, we now have the ability to launch an activity from our custom control with a single tap, completing the user experience for our app dashboard!

Wrapping Up and Next Steps

The Gesture Recognizer features of Xamarin Forms make it easy to add events to both the existing and custom controls you create for your apps. However, you may have noticed that we are using a Lambda expression to subscribe to the Event Handler, and as such don’t have any way to unsubscribe when the control goes out of scope. This is a clear memory leak, and although it’s a small one, we certainly need to address it! We’ll take a closer look at the strategy we used to overcome this problem in our next post. Until then, as always, I hope this was helpful!

The following two tabs change content below.

selaromdotnet

Senior Developer at iD Tech
Josh loves all things Microsoft and Windows, and develops solutions for Web, Desktop and Mobile using the .NET Framework, Azure, UWP and everything else in the Microsoft Stack. His other passion is music, and in his spare time Josh spins and produces electronic music under the name DJ SelArom. His other passion is music, and in his spare time Josh spins and produces electronic music under the name DJ SelArom.