Add GestureRecognizer to row in gridview - xamarin

I have a grid where there are 3 columns containing 2 buttons and 1 label. What I want is to add a Tap gesture to each rows in the grid. I want the click event to be fired when the user taps anywhere in the grid row. Is there any way to do this?
I want to keep my layout as simple as possible. Before I was creating the same grid using multiple stacklayouts and adding gestures to the parent stacklayout. But for performance I want to do this using a grid view.

I would suggest adding a ContentView (which is not as intensive as a StackLayout). Make it cover the whole row and add the GestureRecognizer to that, like so:
<ContentView HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3">
<ContentView.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapped"/>
</ContentView.GestureRecognizers>
</ContentView>

Related

Xamarin Custom Control Command / CommandParameter question

I've been working on creating a custom control for some time. After several iterations I've come to the conclusion that I am having a binding issue... As it is, when I place my control into a simple XAML page and execute the feature, it works just fine. However, when I need to instantiate numerous controls on a single page, i.e. into a collection, flexlayout, carouselview the Command and CommandParameter bindings get lost... and the ViewModel calls no longer occur.
My control is simple... think of a checkbox replacement. I place A 1x1 grid, with a frame (for the outline) and a label to place a single character... "A", "B", "C"... "1". "2". "3"... whatever you would require... I have bindable properties.. Text, TextColor, BorderColor, BackgroundColor, and "Selected".
So, now I need to have a page ask the question... "How do you feel about... whatever... Pick all that apply." Then I provide a list... with number or lettered items... The user can select none, any, or all... So I create a view with a series of questions, that have a list of "checkable" items... As I said above, the control works perfectly if it is in a standalone page... If I generate a List of these controls dynamically, Command and CommandParameter suddenly no longer work.
my test implementation looks something like the following... although in this case think something much simpler like a "lottery ticket" number chooser. In this case the ViewModel would have a simple ObservableCollection<string> PrimaryControlList; And, the CommandParamter will call a VM function along with the text of the control in order to track the items the user has selected.
<Frame x:Name="primaryFrame">
<FlexLayout x:Name="flexPrimary" BindableLayout.ItemsSource="{Binding PrimaryControlList}" Wrap="Wrap" Direction="Row" JustifyContent="SpaceAround" AlignItems="Start" AlignContent="Start" >
<BindableLayout.ItemTemplate>
<DataTemplate>
<local:NumberSelect Text="{Binding .}" Command="Binding DoSomethingWithThis" CommandParameter="{Binding .}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</Frame>
Can anyone provide guidance?
The command is in your ViewModel while the current BindingContext of FlexLayout is PrimaryControlList.
Solution:
First, give a name to your ContentPage, let's say it MyPage:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="MyPage"
x:Class="App266.MainPage">
Assume your page binds to a ViewModel and your binding of command should be:
<local:NumberSelect Text="{Binding .}" Command="{Binding BindingContext.ButtonCommand, Source={x:Reference MyPage}}" CommandParameter="{Binding .}"/>

How can I keep a Xamarin label on screen with data bound to a Carosell Item

I have a
<CarouselView ItemsSource="{x:Static vm:MainPageViewModel.MyItems}">
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding ID}"/>
<Label Text="{Binding Progress}"/>
...
I would like the <Label Text="{Binding Progress}"/> not to scroll off screen, but to stay fixed (at the top) and update as the user swipes through the CarouselView items. If I move the label outside of the CarouselView, it won't be data bound.
How can I keep the label fixed onscreen, but bound to the curren CarouselView item? (Do I need to do this via code behind?)
You can achieve this easily by binding the text property of the Label to the property in your ViewModel which will also do binding to the CurrentItem property of CarouselView.
Can you try something like this:
<Label Text="{x:Static vm:MainPageViewModel.MyCurrentItem.Progress}"/>
<CarouselView CurrentItem="{x:Static vm:MainPageViewModel.MyCurrentItem}"
ItemsSource="{x:Static vm:MainPageViewModel.MyItems}"
... other code
>
... other code
</CarouselView>
and in your ViewModel create a new bindable property of the same type as your MyItems list items are.
I have updated my GitHub repo with the example of this kind of behaviour:
You can find GitHub repo here.
and particular view here.
And here is a GIF of a running demo:
Label is above the CarouselView, and when you swipe and change the selected item, the value of label is changing according to the change of CarouselView.
Hope this was helpful for you, wishing you lots of luck with coding!

How to use a Grid with Bindable Layout (more than one column)

In Xamarin.Forms 3.5 Microsoft introduced us to bindable layouts which can be used to dynamically fill layouts (e.g. StackLayout, Grid, etc.).
To use this in a grid with a single column is pretty straightforward:
<Grid BindableLayout.ItemsSource="{Binding Items}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label Text="{Binding MyProperty}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</Grid>
Now my question is how this can be used to populate a grid with more than one column due to the fact that DataTemplate only allows one view as content. Sure I could but another Grid in it but this would totally nullify the value of bindable layout in a Grid.
Now my question is how this can be used to populate a grid with more than one column due to the fact that DataTemplate only allows one view as content.
From Bindable Layouts, we can see:
While it's technically possible to attach a bindable layout to any layout class that derives from the Layout class, it's not always practical to do so, particularly for the AbsoluteLayout, Grid, and RelativeLayout classes. For example, consider the scenario of wanting to display a collection of data in a Grid using a bindable layout, where each item in the collection is an object containing multiple properties. Each row in the Grid should display an object from the collection, with each column in the Grid displaying one of the object's properties. Because the DataTemplate for the bindable layout can only contain a single object, it's necessary for that object to be a layout class containing multiple views that each display one of the object's properties in a specific Grid column. While this scenario can be realised with bindable layouts, it results in a parent Grid containing a child Grid for each item in the bound collection, which is a highly inefficient and problematic use of the Grid layout.
If you still want to more column, I suggest you can use StackLayout, it can also meet your requirement.
<StackLayout BindableLayout.ItemsSource="{Binding persons}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding name}" />
<Label Text="{Binding age}" />
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
Checking this issue, seems that what you are trying to accomplish can't be done with a Bindable Layout using a Grid as a Element.
The documentation isn't as clear as it should, nevertheless.
You can subscribe to BindingContextChanged event and configure then all the items. You have to configure the grid definitions programatically after the event.

Why listview is taking more space inside scroll view in xamarin forms?

I have label title header, listview and button. All the controls need to scroll in the page. But Listview is taking more space when placed inside scroll view. Could anyone faced the issue like this?
Listview is already have a scrolling behaviour. Why using a view have scrolling property inside into another scrollview..? You can use Scrollview, if you have more than one listviews in your page.
If you still want to use bath views try to set this property of listview HasUnevenRows="True"
As stated before, it is generally a bad idea to have a ListView inside a ScrollView as stated here.
That being said, I replicated this issue and got the page to stop stretching by adding RowHeight to the ListView.
<ScrollView BackgroundColor="Black">
<StackLayout BackgroundColor="LightBlue">
<Label Text="BEFORE"/>
<ListView BackgroundColor="LightGray"
RowHeight="40">
<ListView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>mono</x:String>
<x:String>monodroid</x:String>
<x:String>monotouch</x:String>
<x:String>monorail</x:String>
<x:String>monodevelop</x:String>
<x:String>monotone</x:String>
<x:String>monopoly</x:String>
<x:String>monomodal</x:String>
<x:String>mononucleosis</x:String>
</x:Array>
</ListView.ItemsSource>
</ListView>
<Label Text="AFTER"/>
</StackLayout>
</ScrollView>

Databinding View Model Lines Collection to Canvas (MVVM / Windows Phone)

I'm attempting to allow users to draw on a Canvas by collecting the points as they tap and drag. I'm using an MVVM approach and can successfully collect points, and populate a collection of lines on the View Model. However, I need to be able to display those line controls on a canvas so the user can see the line they are drawing.
My question is, how can I databind the View Models collection of Line controls to the canvas's Children collection?
This can be achieved using an ItemsControl:
<ItemsControl ItemsSource="{Binding CollectionOfLines}">
<!-- specify the panel that the items will be added to -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Line X="{Binding ...}" Y="{Binding ...}" .../>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Resources