I have a ListView and populate it via DataBinding to a Lists Property in my ViewModel. Additionally, I have a menu for the ListView, with a Delete Command, also bound to my ViewModel.
My Problem is now, if I have the ListView initialized, I can delete the lists in it. If I add new lists, I can delete all lists. But then, if I add new items, I can't delete them, because the List I get from the DeleteCommand is the old, already deleted list.
So, after deleting lists, they seem to be somehow, somewhere still present and I can only delete new lists, if the total amount of current lists is higher, than any previous amount of deleted lists.
I hope this is a somehow understandable explanation of my problem.
The Binding is working and Lists Property in my ViewModel holds the correct values, but the "sender" ItemList in the DeleteListCommand is the old ItemList.
Here is my XAML for my ListView:
<ListView x:Name="listView" ItemsSource="{Binding Lists}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell x:Name="viewCell">
<ViewCell.ContextActions>
<MenuItem Command="{Binding BindingContext.RenameListCommand, Source={x:Reference listView}}" CommandParameter="{Binding .}" Text="Rename" />
<MenuItem Command="{Binding BindingContext.DeleteListCommand, Source={x:Reference listView}}" CommandParameter="{Binding .}" IsDestructive="True" Text="Delete" />
</ViewCell.ContextActions>
<ContentView Margin="0,2,0,2"
HeightRequest="50"
BackgroundColor="{Binding Color}">
<ContentView.GestureRecognizers>
<TapGestureRecognizer BindingContext="{Binding Source={x:Reference listView}, Path=BindingContext}"
Command="{Binding ListTappedCommand}"
CommandParameter="{Binding Source={x:Reference viewCell}, Path=BindingContext}" />
</ContentView.GestureRecognizers>
<ContentView.Content>
<Label Text="{Binding Name}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
TextColor="White"
IsEnabled="True"/>
</ContentView.Content>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And here is my ViewModel:
...
public ObservableCollection<ItemList> lists = new ObservableCollection<ItemList>();
public ObservableCollection<ItemList> Lists
{
get { return lists; }
set
{
lists = value;
OnPropertyChanged("Lists");
}
}
public event PropertyChangedEventHandler PropertyChanged;
...
this.DeleteListCommand = new Command<ItemList>((sender) =>
{
OnDeleteList(sender);
});
...
public ICommand DeleteListCommand { get; set; }
private void OnDeleteList(ItemList itemList)
{
Lists.Remove(itemList);
}
...
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Based on sample project here is what you need to do: Name you cell "viewCell" because you need to pass cell to your model to be able to reset ContextAction.
Change binding on menu item to pass the cell instead of ItemList. Then in model reset context action and get an item from binding context of the cell
XAML:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TobyList_XamarinForms"
xmlns:localVM="clr-namespace:TobyList_XamarinForms.ViewModels"
Title="Toby"
x:Class="TobyList_XamarinForms.Views.MasterPage">
<StackLayout Padding="5" VerticalOptions="FillAndExpand" BackgroundColor="#F9F9F9">
<StackLayout.BindingContext>
<localVM:MasterPageViewModel />
</StackLayout.BindingContext>
<ListView x:Name="listView" ItemsSource="{Binding Lists}" CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell x:Name="viewCell">
<ViewCell.ContextActions>
<MenuItem Command="{Binding Path=BindingContext.DeleteListCommand, Source={x:Reference Name=listView}}" CommandParameter="{Binding Source={x:Reference viewCell}}" Text="Delete" />
</ViewCell.ContextActions>
<ContentView Margin="0,2,0,2"
HeightRequest="50"
BackgroundColor="{Binding Color}">
<ContentView.GestureRecognizers>
<TapGestureRecognizer BindingContext="{Binding Source={x:Reference listView}, Path=BindingContext}"
Command="{Binding ListTappedCommand}"
CommandParameter="{Binding Source={x:Reference viewCell}, Path=BindingContext}" />
</ContentView.GestureRecognizers>
<ContentView.Content>
<Label Text="{Binding Name}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
TextColor="White"/>
</ContentView.Content>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout Orientation="Horizontal" HeightRequest="30" Margin="7">
<Label Text="Add">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding AddListCommand}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
</StackLayout>
</ContentPage>
Model:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
using TobyList_XamarinForms.Models;
using Xamarin.Forms;
using System.Linq;
namespace TobyList_XamarinForms.ViewModels
{
public class MasterPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<ItemList> lists = new ObservableCollection<ItemList>();
public ObservableCollection<ItemList> Lists
{
get { return lists; }
set
{
lists = value;
OnPropertyChanged("Lists");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MasterPageViewModel()
{
this.AddListCommand = new Command(() =>
{
OnAddList();
});
//this.DeleteListCommand = new Command<ItemList>((sender) =>
//{
// OnDeleteList(sender);
//});
this.DeleteListCommand = new Command<ViewCell>((sender) =>
{
OnDeleteList(sender);
});
}
public ICommand AddListCommand { get; protected set; }
private void OnAddList()
{
ItemList itemList = new ItemList() { Id = Guid.NewGuid().ToString().ToUpper(), Name = "Lorem Ipsum", Color = "#000000" };
Lists.Add(itemList);
}
public ICommand DeleteListCommand { get; set; }
//public void OnDeleteList(ItemList itemList)
// {
// Lists.Remove(itemList);
// }
public void OnDeleteList(ViewCell viewCell)
{
viewCell.ContextActions.Clear();
Lists.Remove((ItemList)viewCell.BindingContext);
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Related
i have 2 questions
How can i bind a command from MenuItem using Mvvm?
<StackLayout>
<Label Text="{Binding TestText}"/>
<SearchBar TextColor="Black" SearchCommand="{Binding SearchBar}" Text="{Binding TextChanging}"/>
<ListView ItemsSource="{Binding ListOfItems, Mode=TwoWay}" RefreshCommand="{Binding RefreshData}" IsPullToRefreshEnabled="True" IsRefreshing="{Binding IsRefreshing}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextCell TextColor="Black" Text="{Binding MainText}" Detail="{Binding SecondText}" >
<TextCell.ContextActions>
<MenuItem Command="{Binding Path=BindingContext.DeleteCommand}" CommandParameter="{Binding .}" Text="Delete"/>
</TextCell.ContextActions>
</TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public Command DeleteCommand(object sender, EventArgs e)
{
return new Command(() =>
{
});
}
I can't really binding this DeleteCommand using just MVVM.
Question2:
In runtime, my MenuItem look like this:
enter image description here
and i want to look like this:enter image description here
How can i do that?
When you use DeleteCommand to delete a selected item, you need get it and then update the itemsource for the listview.
Here's the code snippet for your reference:
Mode:
public class MyModel
{
public string MainText { get; set; }
}
ViewModel:
public class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<MyModel> ListOfItems { get; set; }
public ICommand DeleteCommand => new Command(Delete);
public MainPageViewModel()
{
ListOfItems = new ObservableCollection<MyModel>()
{
new MyModel(){ MainText = "test1"},
new MyModel(){ MainText = "test2"},
new MyModel(){ MainText = "test3"},
new MyModel(){ MainText = "test4"},
};
}
private void Delete(object obj)
{
//get the item and cast it to Model
var content = obj as MyModel;
ListOfItems.Remove(content);
}
}
Xaml Code:
<StackLayout>
<ListView x:Name="items" ItemsSource="{Binding ListOfItems, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell TextColor="Black" Text="{Binding MainText}" >
<TextCell.ContextActions>
<MenuItem Command="{Binding Path=BindingContext.DeleteCommand , Source={x:Reference Name=items }}" CommandParameter="{Binding .}" Text="Delete"/>
</TextCell.ContextActions>
</TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Tips: Don't forget to add Source={x:Reference Name=items } for the menu item.
Last but not least, bind the View and Model in Code behind or in Xaml:
BindingContext = new MainPageViewModel();
I am trying to MVVM a search bar in Xamarin. I am using MVVMHelpers and EventToCommandBehavior from Xamarin Community Toolkit. I want to turn the TextChanged event to a command.
In my viewmodel file I have some commands to work with the list and an ObservableRangeCollection in which I store "Category" type data. The model only contains Id and Name fields (keeping it simple until I get the hang of it).
I want to be able to search by the Name field of the Category class and display the frames that contains the value from the search bar with no code behind.
I have no idea what to write in the viewmodel file. I saw some tutorials about handling TextChange event in code behind and I tried mvvm-ing it but I had no success.
HomeViewModel.cs
namespace A.point.me.ViewModels
{
public class HomeViewModel : BaseViewModel
{
public ObservableRangeCollection<Category> Categories { get; set; }
public AsyncCommand RefreshCommand { get; }
public Command DelayLoadMoreCommand { get; }
public Command ClearCommand { get; }
public Command LoadMoreCommand { get; }
public Command SearchCommand { get; }
public HomeViewModel()
{
Title = "Home";
Categories = new ObservableRangeCollection<Category>();
LoadMore();
RefreshCommand = new AsyncCommand(Refresh);
ClearCommand = new Command(Clear);
LoadMoreCommand = new Command(LoadMore);
DelayLoadMoreCommand = new Command(DelayLoadMore);
SearchCommand = new Command(Search);
}
void Search()
{
}
void LoadMore()
{
if (Categories.Count >= 20)
return;
Categories.Add(new Category { Name = "Beauty" });
Categories.Add(new Category { Name = "Tattoo" });
Categories.Add(new Category { Name = "Hairstyle" });
Categories.Add(new Category { Name = "Dentist" });
Categories.Add(new Category { Name = "Tech" });
Categories.Add(new Category { Name = "Auto" });
}
void Clear()
{
Categories.Clear();
}
...
Homepage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="A.point.me.Views.HomePage"
xmlns:vm="clr-namespace:A.point.me.ViewModels"
xmlns:model="clr-namespace:A.point.me.Models"
xmlns:cells="clr-namespace:A.point.me.Cells"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:DataType="vm:HomeViewModel"
xmlns:fontAwesome="clr-namespace:FontAwesome"
x:Name="HPage"
Title="{Binding Title}"
BackgroundColor="{AppThemeBinding Dark={StaticResource WindowBackgroundColorDark}, Light={StaticResource WindowBackgroundColor}}">
<ContentPage.BindingContext>
<vm:HomeViewModel />
</ContentPage.BindingContext>
<StackLayout>
<SearchBar x:Name="srcBar">
<SearchBar.Behaviors>
<xct:EventToCommandBehavior EventName="TextChanged" Command="CommandSearch" CommandParameter="{Binding Text, Source={x:Reference srcBar}}"/>
</SearchBar.Behaviors>
</SearchBar>
<RefreshView
Command="{Binding RefreshCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
RefreshColor="Red">
<CollectionView
ItemsSource="{Binding Categories}"
ItemsLayout="VerticalGrid, 2"
BackgroundColor="Transparent"
ItemSizingStrategy="MeasureAllItems"
RemainingItemsThreshold="1"
RemainingItemsThresholdReachedCommand="{Binding DelayLoadMoreCommand}">
<CollectionView.EmptyView>
<StackLayout>
<Label HorizontalOptions="Center" VerticalOptions="CenterAndExpand" Text="No categories yet" Style="{StaticResource LabelLarge}"/>
</StackLayout>
</CollectionView.EmptyView>
<CollectionView.ItemTemplate>
<DataTemplate>
<cells:CategoryCard/>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Header>
<StackLayout Orientation="Horizontal">
<Label HorizontalOptions="CenterAndExpand" Text="CATEGORIES" Style="{StaticResource LabelHeader}"/>
</StackLayout>
</CollectionView.Header>
<CollectionView.Footer>
<StackLayout Orientation="Horizontal">
<Button Text="Clear" Command="{Binding ClearCommand}" HorizontalOptions="CenterAndExpand"/>
</StackLayout>
</CollectionView.Footer>
</CollectionView>
</RefreshView>
</StackLayout>
</ContentPage>
This is where I bind the Name field of the Category class
CategoryCard.xaml
<?xml version="1.0" encoding="UTF-8" ?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="A.point.me.Cells.CategoryCard"
xmlns:models="clr-namespace:A.point.me.Models"
x:DataType="models:Category"
Padding="10">
<Frame Style="{StaticResource CategoryCard}">
<Label VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" Text="{Binding Name}" Style="{StaticResource LabelMedium}"/>
</Frame>
</Grid>
In your xaml change CommandSearch to SearchCommand
<SearchBar x:Name="srcBar">
<SearchBar.Behaviors>
<xct:EventToCommandBehavior EventName="TextChanged" Command="SearchCommand" CommandParameter="{Binding Text, Source={x:Reference srcBar}}"/>
</SearchBar.Behaviors>
</SearchBar>
then in the VM
public HomeViewModel()
{
SearchCommand = new Command<object>(Search);
}
void Search(object obj)
{
var text = (string)obj;
}
Have you tried the Binding extension? Check the code below:
<StackLayout>
<SearchBar x:Name="srcBar">
<SearchBar.Behaviors>
<xct:EventToCommandBehavior EventName="TextChanged" Command="{Binding CommandSearch}" CommandParameter="{Binding Text, Source={x:Reference srcBar}}"/>
</SearchBar.Behaviors>
</SearchBar>
I have a ListView bounded to an ObservableCollection in the ViewModel.
In initialization the ListView items are displayed perfectly.
However, when I try to update a single value of an item of ObservableCollection at run-time, the linked item in the listview does not update automatically. It updates only if I scroll the listView. Why does it behave like this?
Here's the code:
XAML
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Style="{StaticResource frameListView}" >
<StackLayout Margin="-15">
<Label Text="{Binding Articolo.Descrizione}"
Style="{StaticResource labelDescrizioneStyle}" />
<StackLayout Orientation="Horizontal"
HorizontalOptions="End" VerticalOptions="Center">
<Button x:Name="removeButton"
VerticalOptions="Center" HorizontalOptions="Center"
Text="-"
Font="20"
WidthRequest="45" FontAttributes="Bold"
Style="{StaticResource buttonNeutroStyle}"
Command="{Binding Source={x:Reference mieiAcquistiStack}, Path=BindingContext.AggiungiCommand}"
CommandParameter="{Binding .}" />
<StackLayout HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" >
<Label Text="{Binding QtaEdit}"
TextColor="Black"
FontSize="18" FontAttributes="Bold"
WidthRequest="40"
HorizontalTextAlignment="Center" VerticalTextAlignment="Center"
VerticalOptions="FillAndExpand"/>
</StackLayout>
<Button x:Name="addButton"
VerticalOptions="Center" HorizontalOptions="Center"
Text="+"
WidthRequest="45" FontAttributes="Bold"
Style="{StaticResource buttonNeutroStyle}"
Command="{Binding Source={x:Reference mieiAcquistiStack}, Path=BindingContext.AggiungiCommand}"
CommandParameter="{Binding .}" />
</StackLayout>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
ViewModel
public static ObservableCollection<RigaStoricoModel> acquistiList;
public ObservableCollection<RigaStoricoModel> AcquistiList
{
get { return acquistiList; }
set
{
if (acquistiList != value)
{
acquistiList = value;
OnPropertyChanged();
}
}
}
private void AggiungiArticolo(RigaStoricoModel prodotto, ParametriUM parametriUM)
{
double esistenzaUMPredef = parametriUM.EsistenzaUMPredef.GetValueOrDefault(0);
if (esistenzaUMPredef > 0)
{
double qtaMinima = parametriUM.QtaMinima.GetValueOrDefault(1);
if (prodotto.QtaEdit + qtaMinima <= esistenzaUMPredef)
{
prodotto.QtaEdit += qtaMinima; // <-- here the update not working
}
}
Do you want to achieve the result like following GIF?
If so, you should achieve the INotifyPropertyChanged interface in your RigaStoricoModel as Silvermind's said.
Here is MyViewModel.cs code.
public class RigaStoricoModel: INotifyPropertyChanged
{
private double _qtaEdit;
public double QtaEdit
{
set
{
_qtaEdit = value;
OnPropertyChanged("QtaEdit");
}
get => _qtaEdit;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I used AggiungiCommand to make a test(decrease or increase).
public class MyViewModel
{
public static ObservableCollection<RigaStoricoModel> acquistiList;
public ObservableCollection<RigaStoricoModel> AcquistiList
{
get { return acquistiList; }
set
{
if (acquistiList != value)
{
acquistiList = value;
// OnPropertyChanged();
}
}
}
public ICommand AggiungiCommand { protected set; get; }
public ICommand AggiungiCommand2 { protected set; get; }
// public int MyProperty { get; set; }
public MyViewModel()
{
AcquistiList = new ObservableCollection<RigaStoricoModel>();
AcquistiList.Add(new RigaStoricoModel() { QtaEdit=0.28 });
AcquistiList.Add(new RigaStoricoModel() { QtaEdit = 0.38 });
AcquistiList.Add(new RigaStoricoModel() { QtaEdit = 0.48 });
AcquistiList.Add(new RigaStoricoModel() { QtaEdit = 0.58 });
AcquistiList.Add(new RigaStoricoModel() { QtaEdit = 0.68 });
AggiungiCommand2=new Command(async (key) =>
{
RigaStoricoModel model = key as RigaStoricoModel;
model.QtaEdit += 0.1;
});
AggiungiCommand = new Command(async (key) =>
{
RigaStoricoModel model= key as RigaStoricoModel;
model.QtaEdit -= 0.1;
});
}
}
}
Here is layout.xaml(I do not have this style, for testing,I delete them and adjust this layout).
<StackLayout>
<!-- Place new controls here -->
<ListView ItemsSource="{Binding AcquistiList}" x:Name="mieiAcquistiStack" HasUnevenRows="True" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame >
<StackLayout Margin="-15" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<Label Text="{Binding Articolo.Descrizione}"
/>
<StackLayout Orientation="Horizontal"
HorizontalOptions="End" VerticalOptions="Center">
<Button x:Name="removeButton"
VerticalOptions="Center" HorizontalOptions="Center"
Text="-"
Font="20"
WidthRequest="45" FontAttributes="Bold"
Command="{Binding Source={x:Reference mieiAcquistiStack}, Path=BindingContext.AggiungiCommand}"
CommandParameter="{Binding .}" />
<StackLayout HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" >
<Label Text="{Binding QtaEdit}"
TextColor="Black"
FontSize="18" FontAttributes="Bold"
WidthRequest="40"
HorizontalTextAlignment="Center" VerticalTextAlignment="Center"
VerticalOptions="FillAndExpand"/>
</StackLayout>
<Button x:Name="addButton"
VerticalOptions="Center" HorizontalOptions="Center"
Text="+"
WidthRequest="45" FontAttributes="Bold"
Command="{Binding Source={x:Reference mieiAcquistiStack}, Path=BindingContext.AggiungiCommand2}"
CommandParameter="{Binding .}" />
</StackLayout>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Here is layout background code.
public MainPage()
{
InitializeComponent();
this.BindingContext = new MyViewModel();
}
I'm trying to make grouped ListView for meals and foods with Add Food button for each meal. But when Add Food command is executed I don't know to which meal should I add the food. So I thought it would be good to pass the meal name as a parameter but I don't know how.
This is the ViewModel
public class ViewModel : BaseViewModel
{
public FoodViewModel()
{
this.AddFood = new Command<string>(this.OnAddFood);
this.MealGroups = new ObservableCollection<MealGroup>();
}
public ObservableCollection<MealGroup> MealGroups { get; set; }
public ICommand AddFood { get; private set; }
private void OnAddFood(string param)
}
This is the MealGroup
public class MealGroup : ObservableCollection<Food>
{
public string MealName { get; set; }
public ObservableCollection<Food> Foods => this;
}
This is the ListView
<StackLayout x:Name="MealLayout" HorizontalOptions="FillAndExpand">
<Button x:Name="AddMealButton" Text="Add Meal" Command="{Binding AddMeal}"/>
<ListView x:Name="MealGroupsListView" ItemsSource="{Binding MealGroups}" IsGroupingEnabled="true">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<StackLayout x:Name="MealInfoLayout" Orientation="Horizontal">
<Label x:Name="MealNameLbl" Text="{Binding MealName}" />
<StackLayout>
<StackLayout.BindingContext>
<local:FoodViewModel/>
</StackLayout.BindingContext>
<Button x:Name="AddFoodCommand"
Text="Add Food"
Command="{Binding AddFood}"
CommandParameter="{Binding }"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label x:Name="FoodNameLbl" Text="{Binding Name}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Simply pass the name of the property as the Path of your Binding:
<Button x:Name="AddFoodCommand"
Text="Add Food"
Command="{Binding AddFood}"
CommandParameter="{Binding MealName}"/>
Your AddFood command will be called with the MealName property as parameter.
There's one more better way to access whole object(not only name)
<Button x:Name="AddFoodCommand"
Text="Add Food"
Command="{Binding AddFood}"
CommandParameter="{Binding .}"
/>
Through this,CommandParameter will return whole item
Firstly, I should add a foodId on the class. It's more efficient to find the item back with such an instruction :
Mg = MealGroups.First(o => o.Id == SearchedMealGroupId);
In such case, i personnaly use the ItemTapped functionnality of the listview without button (or maybe just an image for UI Experience).
<ListView x:Name="listElement" .... ItemTapped="listElement_ItemTapped" ..../>
and in the .xmal.cs :
private void listElement_ItemTapped(object sender, ItemTappedEventArgs e)
{
int SelectedMealGroupId = ((MealGroup)e.Item).Id; // You are sure of the type of e.Item !
....
// You can call methods of the VM with :
((ViewModel)BindingContext).OnAddFood(SelectedMealGroupId); // Int parameter, not string
}
I am using Prism in my Xamarin forms app, I have a ListView where each Listitem has 2 images, an edit image and delete image.
I have usedTapGesturesRecognizers on these 2 images, and binded _DelegateCommands for these TapGestureRecognizers. However these DelegateCommands do not get called.
Thanks in advance
This is my XAML code
<ListView x:Name="lstAddress" HasUnevenRows="True" SeparatorVisibility="None" ItemsSource="{Binding ListItems}" CachingStrategy="RecycleElement" >
<ListView.Behaviors>
<b:EventToCommandBehavior EventName="ItemTapped"
Command="{Binding ListItemSelectCommand}" EventArgsParameterPath="Item"/>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Description}" FontSize="15" TextColor="#959595">
</Label>
<Image Source="Edit.png" HeightRequest="50" WidthRequest="50" IsVisible="{Binding ShowEdit}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command ="{Binding EditAddressCommand}"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
<Image Source="Delete.png" ClassId="{Binding ListID}" HeightRequest="30" WidthRequest="30" IsVisible="{Binding ShowIcon}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command ="{Binding DeleteAddressCommand}"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In my ViewModel:
public class AddressBookViewModel : BindableBase
{
INavigationService _navigationService;
public DelegateCommand EditAddressCommand { get; set; }
public DelegateCommand DeleteAddressCommand { get; set; }
public ObservableCollection<ListModel> ListItems {get;set;} = new ObservableCollection<ListModel>();
public DelegateCommand<ListModel> ListItemSelectCommand { get; set; }
public MyZypListsViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
EditAddressCommand = new DelegateCommand(EditAddress);
DeleteAddressCommand = new DelegateCommand(DeleteAddress);
ListItemSelectCommand = new DelegateCommand<ListModel>(ListItemSelected);
}
private void EditAddress()
{
//Edit listitem
}
private void DeleteAddress()
{
//Delete listitem
}
}
For each item on your list, your BindingContext is not the same as the ListView's BindingContext. Locally your BindingContext is the item itself, this is why you can obtain the 'Description' property, for example. Xamarin is searching for an ICommand called EditAddressCommand in your item, not in your view model.
You can make a Cross Binding reference if you want... Just replace your commands at item template to this way:
Command ="{Binding BindingContext.EditAddressCommand, Source={x:Reference lstAddress}"
It may work.