Handling User Interaction Using Xamarin Forms (XF)

(Part 6 of the Panzer General Portable Project)

Now that I have a rudimentary board in place, I need to understand basic user interaction with the board. The most common gesture is the tap. When I creted the Windows Phone 6/7 version of this game using native WPF, it was very easy to work with these concepts. The Xamarin Forms (XF), not so much.

In XF, capturing a user’s tap on the screen is done via the TapGestureRecognizer class. For example, to see if a person taps on an given game hex you can write some code like this (lines 1 and 19 are the important ones below):

1 let tapRecognizer = new TapGestureRecognizer() 2 3 let getTerrainFrame (tile: Tile) (scale:float) = 4 let baseTile = getBaseTile tile 5 let baseTerrain = getBaseTerrainFromTerrain baseTile.Terrain 6 let tileId = baseTerrain.Id 7 let locatorPrefix = 8 match baseTerrain.Condition with 9 | LandCondition.Dry -> "tacmapdry" 10 | LandCondition.Frozen -> "tacmapfrozen" 11 | LandCondition.Muddy -> "tacmapmuddy" 12 let frame = new TileFrame(tile) 13 let terrainImageLocator = locatorPrefix + tileId.ToString() 14 let image = getImage terrainImageLocator 15 image.Scale <- (scale + 0.6) 16 frame.BackgroundColor <- Color.Transparent 17 frame.BorderColor <- Color.Transparent 18 frame.Content <- image 19 frame.GestureRecognizers.Add(tapRecognizer) 20 frame 21

The getTerrainFrame is called for each hex that is created for the game board

Since there can be multiple frames layered on top of the base terrain image (like units, nation flags, etc..) we need a way for those frames to not capture the tap event. Enter the InputTransparent property (line 8 below)

1 let getSingleUnitFrame (iconId:int) (scale:float) = 2 let frame = new Frame() 3 let path = "tacicons" + iconId.ToString() 4 let image = getImage path 5 image.Scale <- (scale + 0.6) 6 frame.BackgroundColor <- Color.Transparent 7 frame.BorderColor <- Color.Transparent 8 frame.InputTransparent <- true 9 frame.Content <- image 10 Some frame 11

With each hex’s base frame now wired up to this tap recognizer, I need a way to send data into the event and a way to get the data out of the event.

My first thought was to use the eventArgs – which is the way I have done it 100% of the time before this project. I was thinking code like this:

1 type TapEventArgs(tileId:int) = 2 inherit EventArgs() 3 member this.TileId = tileId 4

Where I have an custom event type that inherits from EventArgs and can can put whatever data I want into the additional properities – in this case the unique Id for the Hex/Tile

I could then create an event handler that handles the event and gets the needed data from the event args:

1 let handleTapEvent (sender:Object) (e:TapEventArgs) = 2 app.MainPage.DisplayAlert(e.TileId.ToString(), "OK") |> ignore 3 () 4

And then to wire things together, I would use an EventHandler class

1 let tapEventHandler = new EventHandler<TapEventArgs>(handleTapEvent) 2 tapRecognizer.Tapped.AddHandler(tapEventHandler :> EventHandler<EventArgs>) 3

Unfortunately, this does not work!  When doing a simple cast, I get

The type ‘EventArgs’ is not compatible with the type ‘TapEventArgs’

and if I try and force it into the event handler

1 let tapEventHandler = new EventHandler<TapEventArgs>(handleTapEvent)

I get this error

This expression was expected to have type ‘EventHandler’ but here has type ‘EventHandler<TapEventArgs>’

You can see my trail of tears on stack overflow here

So instead of spending my time fighting with the compiler, I decided to use the other event arg: object

1 let handleTapEvent (sender:Object) (e:EventArgs) = 2 let tileFrame = sender 😕> TileFrame 3 let tile = tileFrame.Tile 4 let baseTile = getBaseTile tile 5 let tileId = baseTile.Id.ToString() 6 app.MainPage.DisplayAlert("Tile Pressed", tileId, "OK") |> ignore 7 () 8

with the TileFrame inheriting from Frame like so

1 type TileFrame(tile:Tile) = 2 inherit Frame() 3 member this.Tile = tile 4

and I get what I need

image

2 Responses to Handling User Interaction Using Xamarin Forms (XF)

  1. Pingback: Dew Drop - December 13, 2018 (#2859) - Morning Dew

  2. Pingback: F# Weekly #50, 2018 – Bolero: F# Tools for WebAssembly! – Sergey Tihon's Blog

Leave a comment