Xamarin Forms TapGestureRecognizer Is not issuing command - c#

So I have a xamarin forms app that currently only implemented for android. I am attempting to implement a tap event. When tapped though this never hits the command in the ViewModel. I'm not sure if I have something the matter with my code or I am just implementing it wrong.
ViewModel Code:
private RelayCommand<object> _OnClickableLabel;
public RelayCommand<object> OnClickableLabel
{
get { return _OnClickableLabel ?? (_OnClickableLabel = new RelayCommand<object>((currentObject) => Test(currentObject))); }
}
private void Test(object currentObject)
{
Application.Current.MainPage.DisplayAlert("Alert", "were going down cap", "OK");
}
Page Xaml:
<ListView Grid.Row="1" ItemsSource="{Binding Notifications}" RowHeight="100">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" BackgroundColor="{Binding BackgroundColor}">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnClickableLabel }" />
</StackLayout.GestureRecognizers>
<Label FontSize="Large" Text="{Binding Title}"></Label>
<Label FontSize="Small" Text="{Binding Text}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have tested it using a method in the page's cs code and that works fine but it has to be implemented in the ViewModel because it affects that data.

From your description, you want to add a tap gesture recognizer in ListView, and want to pass ListView current row data to TapGestureRecognizer event, am I right?
If yes, as Jason's opinion, you need to take a look Xamarin.Forms Relative Bindings firstly,name ListView as listview1, then take a look the following code:
<ListView
x:Name="listview1"
Grid.Row="1"
ItemsSource="{Binding Notifications}"
RowHeight="100">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="{Binding BackgroundColor}" Orientation="Vertical">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.OnClickableLabel, Source={x:Reference listview1}}" CommandParameter="{Binding .}" />
</StackLayout.GestureRecognizers>
<Label FontSize="Large" Text="{Binding Title}" />
<Label FontSize="Small" Text="{Binding Text}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public partial class Page16 : ContentPage
{
public ObservableCollection<Notclass> Notifications { get; set; }
public ICommand OnClickableLabel { get; set; }
public Page16()
{
InitializeComponent();
Notifications = new ObservableCollection<Notclass>()
{
new Notclass(){Title="title 1",Text="notification 1"},
new Notclass(){Title="title 2",Text="notification 2"},
new Notclass(){Title="title 3",Text="notification 3"},
new Notclass(){Title="title 4",Text="notification 4"},
new Notclass(){Title="title 5",Text="notification 5"}
};
OnClickableLabel = new Command(n=> {
var vm = (Notclass)n;
Application.Current.MainPage.DisplayAlert("Alert",vm.Title , "OK");
});
this.BindingContext = this;
}
}
public class Notclass
{
public string Title { get; set; }
public string Text { get; set; }
public Color BackgroundColor { get; set; } = Color.White;
}

Related

Xamarin ListView not updating with MVVM design pattern

I'm trying to add items to a listview from an Entry that is in a frame of its own though the listview won't update. The entry for reference, is added to the UI using the code behind. Here's the listview:
<ListView ItemsSource="{Binding TodoListItems}" x:Name="todoList" HeightRequest="400">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<FlexLayout JustifyContent="SpaceBetween" Padding="20,0">
<ContentView>
<FlexLayout AlignItems="Center" >
<input:CheckBox IsChecked="{Binding Complete}"
CheckChangedCommand="{Binding Path=BindingContext.CompleteTodoCommand, Source={x:Reference todoList}}"
CommandParameter="{Binding .}"
/>
<Label Text="{Binding TodoText}" Padding="10,0,0,0" FontSize="Large"/>
</FlexLayout>
</ContentView>
<ImageButton Source="trash_icon.png"
Command="{Binding Path=BindingContext.RemoveTodoCommand, Source={x:Reference todoList}}"
CommandParameter="{Binding .}"
Scale="1.2" BackgroundColor="White"
/>
</FlexLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The frame:
<Frame x:Name="bottomSheet" HasShadow="true" CornerRadius="8" Padding="1,4,1,0" BackgroundColor="DarkSlateGray"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=.85,Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width,Factor=1,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=0}"
>
<StackLayout x:Name="bottomSheetStackLayout">
<Button Text="Add new Todo" x:Name="addNewTodoButton" Clicked="AddNewTodoButton_Clicked"/>
</StackLayout>
</Frame>
The ViewModel:
public ObservableCollection<TodoItem> TodoListItems { get; set; }
public ObservableCollection<TodoItem> CompletedTodoItems { get; set; }
public TodoListViewModel()
{
TodoListItems = new ObservableCollection<TodoItem>();
TodoListItems.Add(new TodoItem("Walk the duggo", false));
TodoListItems.Add(new TodoItem("Do the washing", false));
TodoListItems.Add(new TodoItem("Brush off Cheeto dust", false));
CompletedTodoItems = new ObservableCollection<TodoItem>();
//CompletedTodoItems.Add(new TodoItem("Do the dishes",true));
}
public ICommand AddTodoCommand => new Command(AddTodoItem);
public string NewTodoInputValue { get; set; }
void AddTodoItem()
{
TodoListItems.Add(new TodoItem(NewTodoInputValue, false));
}
And the code behind:
private void AddNewTodoButton_Clicked(object sender, System.EventArgs e)
{
Entry addTodoEntryField = new Entry();
bottomSheetStackLayout.Children.Remove(addNewTodoButton);
bottomSheetStackLayout.Children.Add(addTodoEntryField);
addTodoEntryField.Completed += AddTodoEntryField_Completed;
}
private void AddTodoEntryField_Completed(object sender, EventArgs e)
{
Entry addTodoEntryField = (Entry)sender;
Console.WriteLine("The text in the entry field: "+addTodoEntryField.Text);
addTodoEntryField.BindingContext = ViewModel;
ShowTodoItemsText(ViewModel.TodoListItems);
addTodoEntryField.SetBinding(Entry.TextProperty, "NewTodoInputValue");
Console.WriteLine(value: ViewModel.NewTodoInputValue==null);
ViewModel.AddTodoCommand.Execute(null);
Console.WriteLine(ViewModel.TodoListItems.Count);
//Console.WriteLine(ViewModel.TodoListItems.Last().TodoText==null);
ViewModel.TodoListItems.Add(new TodoItem("Added manually", false));
}
Edit: To clarify, I have a frame with a button, which when clicked is replaced with an Entry, the completed text of which, I want to bind to the TodoListItems observable collection, which is displayed by the listView.
I've checked that there are objects being put into the observable collection. I'm not implementing INotifyPropertyChanged on the ViewModel yet though as the collection has that. (Also, the Entry at the top of the page does work fine.) Any help appreciated.

Passing data from viewmodel to viewmodel and use it .Net maui

How to use viewmodel get data and draw graphic,Can I send data from mainpageviewmodel to use on graphicviewmodel and show on graphicpage
MainPage.xaml
<CollectionView
BackgroundColor="Transparent"
ItemsSource="{Binding graphiclist}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:GRAPHICModel">
<Grid Padding="0">
<Frame HeightRequest="100">
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=DrawGRAPHCommand}" CommandParameter="{Binding .}" />
</Frame.GestureRecognizers>
<Grid Padding="0">
<VerticalStackLayout>
<Label Style="{StaticResource BoldLabel}" Text="{Binding GraphicNamae}" />
</VerticalStackLayout>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
mainpageviewmodel.cs
public partial class MainpageViewModel : ObservableObject
{
public ObservableCollection<GRAPHICModel> graphiclist { get; set; }
public MainpageViewModel()
{
graphiclist = new ObservableCollection<GRAPHICModel>
{
new GRAPHICModel { GraphicName= "Name1", Path = "somecoordinate" },
new GRAPHICModel { GraphicName= "Name2", Path = "somecoordinate" },
new GRAPHICModel { GraphicName= "Name3", Path = "somecoordinate" },
new GRAPHICModel { GraphicName= "Name4", Path = "somecoordinate" }
};
}
[ICommand]
async Task DrawGRAPHCommand(GRAPHICModel glist)
{
await Shell.Current.GoToAsync(nameof(DrawPage), true, new Dictionary<string, object>
{
["GRAPHICModel"] = glist
});
}
}
graphicpage.xaml
<VerticalStackLayout>
<Label Text="{Binding Graphiclist.GraphicName}" />
<FlexLayout
AlignItems="Center"
Direction="Column"
JustifyContent="SpaceEvenly">
<VerticalStackLayout>
<GraphicsView
Drawable="{StaticResource drawable}"
HeightRequest="1000"
WidthRequest="1000" />
</VerticalStackLayout>
</FlexLayout>
</VerticalStackLayout>
graphicviewmodel.cs
[QueryProperty(nameof(Graphiclist), "GRAPHICModel")]
public partial class GraphicViewModel : ObservableObject, IDrawable
{
[ObservableProperty]
GRAPHICModel graphiclist;
public void Draw(ICanvas canvas, RectF dirtyRect)
{
something.load(graphiclist.Path)
canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 4;
canvas.DrawPath();
}
}
After tap , can not get graphiclist.path for canvas.DrawPath(-somepoint-); after tap
but can show <Label Text="{Binding Graphiclist.GraphicName}" />
In MVVM for communication between View Models common practice is to use Mediator Design Pattern !! There are a lot articles around it as much as yt videos.

How to get indexes of Xamarin CollectionView Items?

I have a CollectionView:
<CollectionView Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="1" x:Name="SomeCollection"
ItemsLayout="VerticalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal" Padding="3" Spacing="0">
<Label WidthRequest="90" HeightRequest="20" Text="{Binding SomeItemText}" BackgroundColor="Black" TextColor="White"
VerticalTextAlignment="Center" HorizontalTextAlignment="Center" />
<Button WidthRequest="36" HeightRequest="36" CornerRadius="0" ImageSource="plus_thick.xml" BackgroundColor="Green"
Clicked="Duplicate_Clicked" />
<Button WidthRequest="36" HeightRequest="36" CornerRadius="0" ImageSource="close_thick.xml" BackgroundColor="Red"
Clicked="Remove_Clicked" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Every item in the collection has a duplicate and a remove button. By clicking the button I would like to duplicate/remove the item, but CollectionView doesn't have indexes on items.
Could you help me with a custom renderer or even something shorter and simpler?
About getting current collectionview item index by Button command, I do one sample that you can take a look:
<StackLayout>
<CollectionView
x:Name="SomeCollection"
ItemsLayout="VerticalList"
ItemsSource="{Binding items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout
Padding="3"
Orientation="Horizontal"
Spacing="0">
<Label
BackgroundColor="Black"
HeightRequest="20"
HorizontalTextAlignment="Center"
Text="{Binding SomeItemText}"
TextColor="White"
VerticalTextAlignment="Center"
WidthRequest="90" />
<Button
BackgroundColor="Green"
Command="{Binding BindingContext.duplicatecommand, Source={x:Reference SomeCollection}}"
CommandParameter="{Binding}"
CornerRadius="0"
HeightRequest="36"
Text="duplicate item"
WidthRequest="36" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
public partial class Page13 : ContentPage
{
public ObservableCollection<someitems> items { get; set; }
public ICommand duplicatecommand { get; set; }
public Page13()
{
InitializeComponent();
items = new ObservableCollection<someitems>()
{
new someitems(){SomeItemText="test 1"},
new someitems(){SomeItemText="test 2"},
new someitems(){SomeItemText="test 3"},
new someitems(){SomeItemText="test 4"},
new someitems(){SomeItemText="test 5"},
new someitems(){SomeItemText="test 6"}
};
duplicatecommand = new Command<someitems>(getindexfun);
this.BindingContext = this;
}
private void getindexfun(someitems item)
{
int index = items.IndexOf(item);
}
}
public class someitems
{
public string SomeItemText { get; set; }
}
Instead of using click events, I would bind the buttons to a Command<T>.
This way in the ViewModel where you have your ItemsSource, you can just check what the index is of the Item ViewModel you are passing into the command as parameter. So in the ViewModel add something like this:
public Command<ItemViewModel> RemoveItemCommand { get; }
ctor() // this is the ViewModel constructor
{
RemoveItemCommand = new Command<ItemViewModel>(DoRemoveItemCommand);
}
private void DoRemoveItemCommand(ItemViewModel model)
{
// do stuff to remove
// probably something like:
Items.Remove(model);
// or get the index like:
var index = Items.IndexOf(model);
}
Then you can bind this command like so:
<Button
WidthRequest="36"
HeightRequest="36"
CornerRadius="0"
ImageSource="close_thick.xml"
BackgroundColor="Red"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ItemViewModel}}, Path=RemoveItemCommand}"
CommandParameter="{Binding}" />

how to populate a collectionview defined inside a caruselpage Xamarin.Forms

I have a CaruselPage and inside the DataTemplate I have put a CollectionView
<CarouselPage.ItemTemplate>
<DataTemplate>
<ContentPage>
<ContentPage.Content>
<StackLayout>
<CollectionView
ItemsSource="{Binding Cards}"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
EmptyView="Non ci sono Card Formazione"
Margin="10"
x:Name="CV"
>
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="1" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame BorderColor="Black">
<StackLayout>
<Label Text="{Binding DisciplinaCard}" Style="{StaticResource LabelTesStyle}"/>
<Label Text="{Binding DataCard}" Style="{StaticResource LabelTesStyle}"/>
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
</DataTemplate>
</CarouselPage.ItemTemplate>
I manage to populate the carusel page but I can't find how to populate the collection view.
All the solution that I find is for UWP, but I need to do this in xamarin.forms cross platform.
According to your description, you want to add collectionview control in CaruselPage, I do one sample that you can take a look.
Here is the CaruselPage:
<CarouselPage
x:Class="CaruselApp.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<CarouselPage.ItemTemplate>
<DataTemplate>
<ContentPage>
<StackLayout>
<Label Margin="10" Text="{Binding title}" />
<CollectionView ItemsSource="{Binding collections}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="1" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame BorderColor="Black">
<StackLayout>
<Label Text="{Binding Name}" />
<Label Text="{Binding Age}" />
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
</DataTemplate>
</CarouselPage.ItemTemplate>
The Model in CollectionView:
public class CollectionModel
{
public string Name { get; set; }
public int Age { get; set; }
}
The Model in CarouselPage:
public class CaruselModel
{
public ObservableCollection<CollectionModel> collections { get; set; }
public string title { get; set; }
public static ObservableCollection<CaruselModel> carusels { get; set; }
static CaruselModel()
{
carusels = new ObservableCollection<CaruselModel>()
{
new CaruselModel(){title="title 1", collections=new ObservableCollection<CollectionModel>(){ new CollectionModel() { Name="Cherry",Age=12},new CollectionModel() { Name="barry",Age=23} } },
new CaruselModel(){title="title 2", collections=new ObservableCollection<CollectionModel>(){ new CollectionModel() { Name="Annine",Age=18},new CollectionModel() { Name="Wendy",Age=25} } },
new CaruselModel(){title="title 3", collections=new ObservableCollection<CollectionModel>(){ new CollectionModel() { Name="Mattew",Age=12},new CollectionModel() { Name="Leo",Age=23} } },
new CaruselModel(){title="title 4", collections=new ObservableCollection<CollectionModel>(){ new CollectionModel() { Name="Jessie",Age=12},new CollectionModel() { Name="Junior",Age=23} } },
new CaruselModel(){title="title 5", collections=new ObservableCollection<CollectionModel>(){ new CollectionModel() { Name="Jack",Age=12},new CollectionModel() { Name="Land",Age=23} } }
};
}
}
Please don't forget to add the following code in Android Mainactivity or ios AppDelegate, because you use CollectionView.
Forms.SetFlags("CollectionView_Experimental");
Here is the sample at Github, you can download to test.
https://github.com/CherryBu/CarouselApp
her is the screenshot:

Xamarin Forms - how to display JSON array in ListView?

How can I display JSON array to ListView, I'm confused can somebody help me?
I tried these code and it's not working.
my JSON array
{"table":[
{"table_no":"1","order_status":"served"},
{"table_no":"2","order_status":"pending"},
{"table_no":"3","order_status":"served"},
{"table_no":"4","order_status":"served"},
{"table_no":"8","order_status":"served"},
{"table_no":"10","order_status":"served"},
{"table_no":"11","order_status":"served"},
{"table_no":"12","order_status":"served"},
{"table_no":"14","order_status":"pending"},
{"table_no":"16","order_status":"served"}]}
OrderStat.cs (How do i bind this or how do i deserialize it?)
public class OrderStat
{
public string table_no { get; set; }
public string order_status { get; set; }
}
public class RootObject
{
public List<OrderStat> table { get; set; }
}
OrderStatus.xaml
<ListView x:Name="selectOrd" RowHeight="50" SeparatorColor="White"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<StackLayout Orientation="Horizontal" >
<StackLayout Orientation="Horizontal" VerticalOptions="Center">
<Image Source="table.png" Scale="1"/>
<Label Text="{Binding table_no,StringFormat=' Table no.{0:F0}'}" Font="30" TextColor="White" />
</StackLayout>
<StackLayout HorizontalOptions="FillAndExpand" x:Name="BG" VerticalOptions="Center" >
<Label Text="{Binding order_status}" Font="50" TextColor="White" FontAttributes="Bold" HorizontalTextAlignment="Center"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
OrderStatus.xaml.cs
private async void GetStat()
{
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("http://ropenrom2-001-site1.etempurl.com/Restserver/index.php/customer/view_table_orders");
var stat = JsonConvert.DeserializeObject<List<RootObject>>(response);
selectOrd.ItemsSource = stat;
}
This is the right way to show the json in ListView. I've also updated your Xaml too. I just removed the white text color and x:Name from your ListView.
Xaml
<ListView x:Name="selectOrd" RowHeight="50" SeparatorColor="White"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" >
<StackLayout Orientation="Horizontal" VerticalOptions="Center">
<Image Source="table.png" Scale="1"/>
<Label Text="{Binding table_no,StringFormat='Table no.{0:F0}'}" Font="30" />
</StackLayout>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="Center" >
<Label Text="{Binding order_status}" Font="50" FontAttributes="Bold" HorizontalTextAlignment="Center"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
OrderStat Class
[DataContract]
public class OrderStat
{
[DataMember]
public string table_no { get; set; }
[DataMember]
public string order_status { get; set; }
}
[DataContract]
public class RootObject
{
[DataMember]
public List<OrderStat> table { get; set; }
}
MainPage.xaml.cs
public MainPage()
{
InitializeComponent();
GetStat();
}
private async void GetStat()
{
HttpClient client = new HttpClient();
var response = await client.GetAsync("http://ropenrom2-001-site1.etempurl.com/Restserver/index.php/customer/view_table_orders");
var result = await response.Content.ReadAsStringAsync();
var serializer = new DataContractJsonSerializer(typeof(RootObject));
var ms = new MemoryStream(Encoding.UTF8.GetBytes(result));
var data = (RootObject)serializer.ReadObject(ms);
selectOrd.ItemsSource = data.table;
}
Hi Actual you do not define the select order and the also the objects
retrieve from the JSON should be OrderStat .below is the edited code
<StackLayout BackgroundColor="White">
<ListView x:Name="ListView1" RowHeight="60">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" Padding="8,0,8,0">
<Label Text="{Binding ArticleTitle}" TextColor="#000" FontSize="14" LineBreakMode="TailTruncation" />
<Label Text="{Binding description}" TextColor="#000" LineBreakMode="TailTruncation" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
private async void GetStat()
{
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("http://ropenrom2-001- site1.etempurl.com/Restserver/index.php/customer/view_table_orders");
var stat = JsonConvert.DeserializeObject<List<OrderStat>>(response);
ListView1.ItemsSource = stat;
// selectOrd.ItemsSource = stat;
}

Categories