BindingContext with command and parameter binding - c#

I have a ViewModel, from which i construct a collection of objects. I want to be able to bind my objects on my view, while at the same time being able to use my button.
Currently, my properties are correctly bound to my labels (partnerLogo, partnerText, partnerLink). Unfortunately I'm not able to use my button command to send the command parameter that I bind.
ViewModel
Public class CarouselViewModel : INotifyPropertyChanged
{
public CarouselViewModel()
{
partners = new ObservableCollection<Carousel>()
{
new Carousel(){partnerText = "Test123", partnerLogo = "Test123", partnerLink = "Test123" },
};
openWebsite = new Command<string>((key) =>
{
Device.OpenUri(new Uri(key));
});
}
public ObservableCollection<Carousel> partners
{
get;
set;
}
public ICommand openWebsite
{
private set;
get;
}
}
XAML:
<CarouselView ItemsSource="{Binding partners}">
<CarouselView.ItemsLayout>
<GridItemsLayout/>
</CarouselView.ItemsLayout>
<CarouselView.ItemTemplate>
<DataTemplate>
<Frame>
<StackLayout>
<Image Source="{Binding partnerLogo}"/>
<Label Text="{Binding partnerText}"/>
<Button Text="Read" Command="{Binding openWebsite}" CommandParameter="{Binding partnerLink}"/>
</StackLayout>
</Frame>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
How do I access my button command, while at the same time being able to send the commandparameter?

For each item in the CarouselView, the binding context will be underlying data item which is bound to ItemsSource i.e., Carousel. And for CarouselView, the binding context will be your ViewModel class if you have defined it.
To resolve the issue, set the x:Name="mycaroselview" to CarouselView in the XAML and refer it's path reference to the Button command as like below code example.
<CarouselView x:Name="MyCarousel" ItemsSource="{Binding partners}">
<CarouselView.ItemsLayout>
<GridItemsLayout/>
</CarouselView.ItemsLayout>
<CarouselView.ItemTemplate>
<DataTemplate>
<Frame>
<StackLayout>
<Image Source="{Binding partnerLogo}"/>
<Label Text="{Binding partnerText}"/>
<Button Text="Read" Command="{Binding BindingContext.openWebsite, Source={x:Reference Name=MyCarousel}}" CommandParameter="{Binding partnerLink}"/>
</StackLayout>
</Frame>
</DataTemplate>
</CarouselView.ItemTemplate>
Make sure that you must set the binding context either to ContentPage or CarouselView.
Regards,
Dinesh

What you can do is, give a x:name to the content Page that you are using:-
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyProject;assembly= MyProject"
x:Class="MyProject.Views.MyPage"
x:Name="ThisPage">
And then give Source to the Command. Like this. This method will call the Command that is there in the ViewModel of the Current Page. Also, you can use a Command Parameter to know which Item has Triggered the Command.
<CarouselView ItemsSource="{Binding partners}">
<CarouselView.ItemsLayout>
<GridItemsLayout/>
</CarouselView.ItemsLayout>
<CarouselView.ItemTemplate>
<DataTemplate>
<Frame>
<StackLayout>
<Image Source="{Binding partnerLogo}"/>
<Label Text="{Binding partnerText}"/>
<Button Text="Read" Command="{Binding openWebsite, Source={x:Reference Name=ThisPage}}" CommandParameter="{Binding partnerLink}"/>
</StackLayout>
</Frame>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>

Related

XAML Having a Labeltext from AppResources inside a ListView

I try to populate a Labeltext inside a Listview in XAML. But i want the Labeltext coming from the AppResources. I am shure i forgot somewhere a tiny lil detail like a using or namespace.
Anway, here's the XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Arbeitszeitrechner_Forms.Views.WorkdayListPage"
Style="{StaticResource PageStyle}"
Title="{Binding Title}"
xmlns:local="clr-namespace:Arbeitszeitrechner_Forms.ViewModels"
xmlns:model="clr-namespace:Arbeitszeitrechner_Forms.Models"
xmlns:appres="clr-namespace:Arbeitszeitrechner_Forms.Resources">
<!--Titel im WorkdaysViewModel zugewiesen und im AppResources definiert-->
<RefreshView x:DataType="local:WorkdaysViewModel" Command="{Binding LoadWorkdaysCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<CollectionView x:Name="WorkdaysListView"
ItemsSource="{Binding Workdays}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" x:DataType="model:WorkdayList">
<Label
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16">
<Label.Text>
<MultiBinding StringFormat="{}{0} {1:d}">
<Binding Path="Day" />
<Binding Path="Date" />
</MultiBinding>
</Label.Text>
</Label>
<StackLayout Orientation="Horizontal">
<Label x:Name="LblTotalTime"/> <!-- **************THIS LABEL IS THE **** THING THAT I CAN'T POPULATE****************-->
<Label Text="{Binding TimeTotal, StringFormat='{0:hh}:{0:mm}'}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="13" />
</StackLayout>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
NumberOfTapsRequired="1"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:WorkdaysViewModel}}, Path=WorkdayTapped}"
CommandParameter="{Binding .}">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
and the xaml.cs Code behind:
namespace Arbeitszeitrechner_Forms.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class WorkdayListPage : ContentPage
{
WorkdaysViewModel _viewModel;
public WorkdayListPage()
{
InitializeComponent();
BindingContext = _viewModel = new WorkdaysViewModel();
//Normally i would do something like this here, but for some reason i can't:
//LblTotalTime.Text = AppResources.LblTotalTime
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
}
}
The Bot asked me to provide some more details to compensate the much Code i copied. So please ignore the gibbery gabberish i am Texting here.
use the x:Static extension
<Label Text="{x:Static resources:AppResources.LblTotalTime}" />

Xamarin.Forms - Binding a command

I am using ToolKits Expander and I am trying to bind a command, this is what I got so far:
public partial class AssignTaskPage : ContentPage
{
public AssignTaskPage()
{
InitializeComponent();
GetMathSubCatgories = new Command(() => MathSubCatgoriesCommand());
}
public ICommand GetMathSubCatgories { get; private set; }
void MathSubCatgoriesCommand()
{
Console.Write("Here");
}
}
And in my view
<xct:Expander Command="{Binding GetMathSubCatgories}">
<xct:Expander.Header>
<Frame Padding="10" Margin="10" HasShadow="False" BorderColor="LightGray" VerticalOptions="CenterAndExpand">
<StackLayout Orientation="Horizontal">
<Image Source="{Binding icon}" WidthRequest="25" HeightRequest="25"></Image>
<Label Text="{Binding name}" TextColor="{Binding textColor}" FontSize="Large" FontAttributes="Bold" HeightRequest="35" VerticalOptions="CenterAndExpand"></Label>
</StackLayout>
</Frame>
</xct:Expander.Header>
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ListView x:Name="SubCategories" ItemsSource="{Binding subCategories}" ItemSelected="SubCategories_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding name}" TextColor="#02cc9d" FontAttributes="Bold" HeightRequest="35" VerticalOptions="CenterAndExpand"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</xct:Expander>
This does not work at all, (I put a break point on Console.Write("Here"); and its not hitting it)
So I did some digging and found this tutorial:
https://www.syncfusion.com/kb/12154/how-to-bind-command-to-expander-in-itemtemplate-of-xamarin-forms-listview-sflistview
and here is the sample in git.
https://github.com/SyncfusionExamples/command-to-expander-in-itemtemplate-listview-xamarin
I understand what I have to do here, the problem I am facing is when this Command is called, I was looking to get a value and use it in my AssignTaskPage, but what the tutorial is saying to have a ViewModel which is in a separate file. So should I setup a MessagingCenter in my AssignTaskPage and call it in the ViewModel to get the value I want and pass it to AssignTaskPage?
Because your command isn't defined in the ViewModel that you're binding to.You could bind the command which is defined in your AssignTaskPage,and then bind the viewmodel for the parent element of the expander.
For example :
public partial class AssignTaskPage : ContentPage
{
public AssignTaskPage()
{
InitializeComponent();
GetMathSubCatgories = new Command(() => MathSubCatgoriesCommand());
BindingContext = this;
}
public ICommand GetMathSubCatgories { get; private set; }
void MathSubCatgoriesCommand(object obj)
{
DisplayAlert("Alert!", "" + (obj as Contact).ContactName + "expanded", "Ok");
}
}
the xaml (here use the xaml codes of the above sample),the grid bind the viewmodel,and your expander bind the command of the root (your page):
<?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:ExpanderXamarin"
x:Class="ExpanderXamarin.ExpandableListView"
x:Name="root"
xmlns:sflistview="clr-namespace:Syncfusion.ListView.XForms;assembly=Syncfusion.SfListView.XForms"
xmlns:expander="clr-namespace:Syncfusion.XForms.Expander;assembly=Syncfusion.Expander.XForms">
<ContentPage.Content>
<Grid x:Name="mainGrid" BackgroundColor="#F0F0F0" Padding="4">
<Grid.BindingContext>
<local:ViewModel />
</Grid.BindingContext>
<sflistview:SfListView x:Name="listView" AutoFitMode="DynamicHeight" ItemsSource="{Binding ContactsInfo}">
<sflistview:SfListView.ItemTemplate>
<DataTemplate>
<Frame x:Name="frame" CornerRadius="2" Padding="{OnPlatform Android=1, iOS=1, UWP=0}" Margin="{OnPlatform Android=1, iOS=1, UWP=0}" OutlineColor="White" HasShadow="{OnPlatform Android=true, iOS=false, UWP=true}">
<Grid Padding="{OnPlatform Android=2, iOS=2, UWP=0}" Margin="{OnPlatform Android=1, iOS=1, UWP=0}" BackgroundColor="White" >
<expander:SfExpander x:Name="expander" HeaderIconPosition="None">
<expander:SfExpander.Behaviors>
<local:EventToCommandBehavior Command="{Binding Path=BindingContext.GetMathSubCatgories, Source={x:Reference root}}" EventName="Expanding" CommandParameter="{Binding .}"/>
</expander:SfExpander.Behaviors>
<expander:SfExpander.Header>
...
</expander:SfExpander.Header>
<expander:SfExpander.Content>
..
</expander:SfExpander.Content>
</expander:SfExpander>
</Grid>
</Frame>
</DataTemplate>
</sflistview:SfListView.ItemTemplate>
</sflistview:SfListView>
</Grid>
</ContentPage.Content>
</ContentPage>
if you want get the parameters you could bind the CommandParameter like above.

How to get the index of a clicked CollectionView element?

I have elements added inside a CollectionView. Inside this CollectionView, I have an ImageButton which make visible other ImageButton. I need to get the index of the element by clicking the first ImageButton and use it in the Command of the second ImageButton. Is that possible?
My xaml:
<StackLayout>
<CollectionView ItemSource="{Binding myItemSource}">
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView>
<ImageButton x:Name="FirstImageButton" Command={Binding MakeVisibleNewButton}/>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<ImageButton x:Name="SecondImageButton" Command={Binding xxxCommand} IsVisible={Binding VisibleByFirstButton} />
</StackLayout
As Jason said,you could get the index by looking up the selected item in the ItemSource.
For example:
public class YourViewModel
{
public List<yourmodel> myItemSource{ get; set; }
public ICommand MakeVisibleNewButton{ get; set; }
public YourViewModel()
{
... //fill the myItemSource
MakeVisibleNewButton= new Command<Productor>(ImageButtonClicked);
}
private void ImageButtonClicked(yourmodel obj)
{
int index = myItemSource.IndexOf(obj); //the index is what you want to get
}
}
the xaml:
<StackLayout>
<CollectionView x:Name ="collectionview" ItemSource="{Binding myItemSource}">
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView>
<ImageButton x:Name="FirstImageButton" Command="{Binding BindingContext.MakeVisibleNewButton,Source={x:Reference collectionview}}" CommandParameter="{Binding .}"/>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<ImageButton x:Name="SecondImageButton" Command={Binding xxxCommand} IsVisible={Binding VisibleByFirstButton} />
</StackLayout

How to Bind a command of child class object declared in Main context ViewMOdel xamarin forms

I am having a page(MyPage) and Corresponding view Model(MypageViewModel). I have another view model called(listItemLogicVM) and its declared as observable collection in MypageViewModel.
MyPage XAML
<ListView x:Name="lstvwItemSelected" ItemsSource="{Binding
LstlistItemLogicVM}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<StackLayout>
<Entry Text="{Binding RequestingQty,Mode=TwoWay}"/>
<Image Source="ArrowUp.png"><Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding QtyDecrementCommand}"/>
</Image.GestureRecognizers>
</Image>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MypageViewModel
public class MypageViewModel
{
ObservableCollection<listItemLogicVM> lstvwItemSelected = new ObservableCollection<listItemLogicVM>()
.
-- OtherLogics
.
}
listItemLogicVM
public class listItemLogicVM
{
public Command QtyDecrementCommand { get; set; }
public int RequestingQty
{
get { return _requestingQty; }
set { _requestingQty = value; OnPropertyChanged();}
}
}
Above Binding throws error. Can anyone help me to bind the command and property of listItemLogicVM to the list view in MyPage XAML
It looks to me like your listview XAML is incorrect. Try this (note the only changes are to the x:Name and ItemsSource attribute of your ListView):
<ListView x:Name="myUniqueListName" ItemsSource="{Binding
lstvwItemSelected}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<StackLayout>
<Entry Text="{Binding RequestingQty,Mode=TwoWay}"/>
<Image Source="ArrowUp.png"><Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding QtyDecrementCommand}"/>
</Image.GestureRecognizers>
</Image>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
That should call the QtyDecrementCommand on the item that is tapped in the list. This assumes that you've correctly assigned some code to that command for each item in the list.
You need to add command in your MypageViewModel and your xaml code look like :
<TapGestureRecognizer Command="{Binding Source={x:Reference lstvwItemSelected}, Path=BindingContext.QtyDecrementCommand}"/>
</Image.GestureRecognizers>

Using Tap Gestures on images within listview

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.

Categories

Resources