Accessing name when toggling switch in listview xamarin forms - xamarin

I have the below codes which consist of a list view. The listview contains a label and a custom switch.
<ListView x:Name="myListView" Margin="20" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell IsEnabled="False">
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackLayout VerticalOptions="CenterAndExpand">
<Label Text="{Binding .}" />
</StackLayout>
<local:CustomSwitch x:Name="customSwitch"
SwitchOffColor="Gray"
SwitchOnColor="Red"
SwitchThumbColor="White"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Grid.Column="1"
/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
What I want to achieve is to get the label text when I toggle the switch. How can In achieve this in Xamarin Forms from my ViewModel ?

The simplest way would be to create a Bindable property in your CustomSwitch like
BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(object), null);
public object CommandParameter
{
get { return GetValue(CommandParameterProperty ); }
set
{
SetValue(CommandParameterProperty , value);
}
}
Then in your xaml adapt your declaration to
<local:CustomSwitch x:Name="customSwitch"
SwitchOffColor="Gray"
SwitchOnColor="Red"
SwitchThumbColor="White"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Grid.Column="1"
CommandParameter={Binding .}
/>
Now in your codebehind you can easily access and consume the CommandParameter and cast it to string:
string labelName = (string) ((sender as CustomSwitch).CommandParameter);

Related

Xamarin Forms CollectionView TapGestureRecognizer not firing on label

I have a XF app with the following collection view defined. The 2nd label has a TapGestureRecognizer that doesn't fire DoSomethingInteresting in the model when I tap the label (trying this on Android). Can someone see what the issue is please?
A working sample can be cloned from here.
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:SampleApp"
x:Class="SampleApp.MainPage">
<StackLayout>
<CollectionView ItemsSource="{Binding GaugeSites}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Label Grid.Column="0"
Text="{Binding Description}"
FontSize="20"
Margin="10"
TextColor="Black"
FontAttributes="Bold" />
<Label Grid.Column="1"
Margin="10"
FontSize="20"
Text="Click Me!">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding DoSomethingInteresting}" />
</Label.GestureRecognizers>
</Label>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
Model
namespace SampleApp
{
public class MainPageModel : FreshBasePageModel
{
public MainPageModel() : base()
{
GaugeSites = new List<GaugeSite>();
for (var index = 1; index <= 5; index++)
{
GaugeSites.Add(new GaugeSite()
{
Description = $"Gauge Site {index}"
});
}
}
public List<GaugeSite> GaugeSites { get; set; }
public Command DoSomethingInteresting
{
get
{
return new Command(() =>
{
});
}
}
}
[AddINotifyPropertyChangedInterface]
public class GaugeSite
{
public string Description { get; set; }
}
}
Maybe this is redundant but I will take my chances. As other answers indicated you need to set the source within your TapGestureRecognizer to the name of the CollectionView. However, it is probably useful to know which GaugeSite instance was associated with the CollectionView item whose TabGestureRecognizer was tapped. To add this info add a CommandParamter, i.e.
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.DoSomethingInteresting,
CommandParameter="{Binding}"
Source={x:Reference YourCollectionName}}" />
</Label.GestureRecognizers>
When it comes to the command you could use
DoSomethingInteresting = new Command<GaugeSite>((a) => DoSomething(a));
in your viewmodels contructor. And the method referenced by the lambda would be
void DoSomething(GaugeSite gaugeSite)
{
// ....
}
Hope this helps
I have download your sample, please take a look the following step.
1.Binding MainpageModel for MainPage bindingContext, add this line in MainPage.cs.
this.BindingContext = new MainPageModel();
2.Name Collectionview as collection1, then modify label command.
<CollectionView x:Name="collection1" ItemsSource="{Binding GaugeSites}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Label
Grid.Column="0"
Margin="10"
FontAttributes="Bold"
FontSize="20"
Text="{Binding Description}"
TextColor="Black" />
<Label
Grid.Column="1"
Margin="10"
FontSize="20"
Text="Click Me!">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.DoSomethingInteresting, Source={x:Reference collection1}}" />
</Label.GestureRecognizers>
</Label>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Then you can try again.

How can we set a default color `#c4bfc7` for row records displaying in the Xamarin forms page

Where do we set a default color #c4bfc7 for row records displaying in the page, below is the xaml. Now while tapping, it is showing some color, which is fine. But I want to display some color for rows by default
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding image}" WidthRequest="50" HeightRequest="50" Grid.Column="0" VerticalOptions="Center"/>
<StackLayout Grid.Column="1">
<Label Text="{Binding FullName}" TextColor="White" HorizontalTextAlignment="Start"/>
</StackLayout>
<StackLayout Grid.Column="2">
<Label Text="{Binding SoccerStatus}" HorizontalTextAlignment="Center" TextColor="White"/>
</StackLayout>
<StackLayout Grid.Column="3">
<Label Text="{Binding CurrentDate}" HorizontalTextAlignment="Center" TextColor="White"/>
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
UPDATE
Your grid inside the ViewCell would look something like below:
<Grid BackgroundColor="#c4bfc7">
First of all, you might wanna add a property for hex code in your model that is bound to your ListView
public class ListModel
{
public string HexCode{get; set;}
}
Now when you are filling this model based on your conditions push in the correct hex code that you want as that items background colour. Once you are done with that your listview would look something like below:
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor={Binding HexCode}>
Then add a convertor that converts hex to colour:
public class HexToColorConvertor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string colorHex = (string)value;
if (string.IsNullOrEmpty(colorHex) && string.IsNullOrWhiteSpace(colorHex))
return Color.White;
return Color.FromHex(colorHex); //Note that if the hex code is not valid this might crash
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Not implemented as we do not convert back
throw new NotSupportedException();
}
}
Now add this convertor to your resources like below:
<common:HexToColorConvertor x:Key="HexToColor" />
Where common is the namespace of that convertor and then use it like below:
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor={Binding HexCode, Converter={StaticResource HexToColor}}>
You should set #c4bfc7 background colour to outmost view which is is grid inside view cell here.
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="#c4bfc7">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding image}" WidthRequest="50" HeightRequest="50" Grid.Column="0" VerticalOptions="Center"/>
<StackLayout Grid.Column="1">
<Label Text="{Binding FullName}" TextColor="White" HorizontalTextAlignment="Start"/>
</StackLayout>
<StackLayout Grid.Column="2">
<Label Text="{Binding SoccerStatus}" HorizontalTextAlignment="Center" TextColor="White"/>
</StackLayout>
<StackLayout Grid.Column="3">
<Label Text="{Binding CurrentDate}" HorizontalTextAlignment="Center" TextColor="White"/>
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
Furthermore If you want to change the list view selected item color.
Here is an answer which worked for me. It includes adding few keys to android's style.xml
https://stackoverflow.com/a/38457882/4707196

Xamarin form: BindingProperty not setting the property

I am having trouble on my custom control with binding property. It used to be worked when pcl project. After pulling the code out into .net standard 2.0 with latest xamarin form package, it's not working.
This is the setup i have
public static readonly BindableProperty ChildProperty = BindableProperty.Create(nameof(Child), typeof(ChildModel), typeof(ChildModel), null, BindingMode.OneWay, propertyChanging: (BindableObject bindable, object oldValue, object newValue) => {
var a = newValue;
});
and the property is
public ChildModel Child
{
get
{
return (ChildModel)GetValue(ChildProperty);
}
set
{
SetValue(ChildProperty, value);
}
}
I can see newValue does have the childModel data passing in the callback. The GetValue of the second set of code always return null.
<ListView
Style="{StaticResource listStyle}"
AutomationId="listChildren"
CachingStrategy="RecycleElement"
x:Name="childListView"
ItemSelected="OnItemSelected"
ItemTapped="OnItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Frame HasShadow="false" Padding="{StaticResource cellPadding}">
<local:ExtendedFrame Style="{StaticResource cardStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource profileGridSize}"></ColumnDefinition>
<ColumnDefinition>*</ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:CircleImage
Grid.Row="0"
Grid.Column="0"
Style="{StaticResource profileImageStyle}"
Source="{Binding Source}"
VerticalOptions="Center"
HorizontalOptions="Center">
</controls:CircleImage>
<StackLayout Orientation="Vertical"
Grid.Row="0"
Grid.Column="1"
VerticalOptions="Center"
HorizontalOptions="Start">
<Label AutomationId="aChildName" Style="{StaticResource MediumBoldFont}" x:Name="childName" Text="{Binding DisplayName}" HorizontalOptions="StartAndExpand" />
<local:ChildInfoIconsView
Child="{Binding .}"
VerticalOptions="Fill">
</local:ChildInfoIconsView>
</StackLayout>
</Grid>
</local:ExtendedFrame>
</Frame>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Get the click command from template to the view model in xamarin

In the template there is a button and created as below called x:Name="cartbutton"
<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
x:Class="FormsApplication.Templates.ListItemTemplate"
xmlns:controls="clr-namespace:FormsControls.Base;assembly=FormsControls.Base"
ColumnSpacing="0" RowSpacing="0" HeightRequest="350" Padding="8,0,8,8" x:Name="ListItemTempPage">
<Grid.RowDefinitions>
<RowDefinition Height="300" />
<RowDefinition Height="30" />
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:RoundedBoxView Grid.RowSpan="2" BackgroundColor="White" BorderColor="Silver" CornerRadius="8" BorderThickness="2"/>
<ffimageloading:CachedImage Grid.RowSpan="2" Source="{Binding ImageSource}" Aspect="AspectFill" DownsampleToViewSize="False"/>
<Button x:Name="cartbutton" Grid.Row="0" HorizontalOptions="End" VerticalOptions="Start" Image="lowim.png" BackgroundColor="Transparent" Margin="0,5,5,0" Command="{Binding ParentContext.Itemtapped, Source={x:Reference ListItemTempPage}}" CommandParameter="{Binding .}"/>
</Grid>
and the parent page called by the reference the template as below
<ListView x:Name="listView" Grid.Row="1" Margin="0,8,0,0"
HasUnevenRows="true" ItemsSource="{Binding FoodItems}"
SeparatorColor="Transparent" SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<templates:ListItemTemplate ParentContext="{Binding BindingContext, Source={x:Reference ListItemPage}}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Behaviors>
<controls:EventToCommandBehavior EventName="ItemTapped" Command="{Binding ListItemTappedCommand}" CommandParameter="{x:Reference listView}"/>
</ListView.Behaviors>
</ListView>
finally the viewmodel goes with the icommand as below
public SelectItemViewModel(IAppContext context, IRestService restService) : base(context)
{
_restService = restService;
Title = "Category Products";
ListItemTappedCommand = new Command<ListView>(OnListItemTappedCommand);
PageAppearingCommand = new Command(OnPageAppearingCommand);
Itemtapped = new Command(OnItemtapped);
}
public ICommand Itemtapped { get; }
private void OnItemtapped()
{
Navigator.PushAsync<SelectItemViewModel>();
}
so the probelm is when compiling there is an error saying that
No property, bindable property, or event found for 'ParentContext', or mismatching type between value and property.
so basically saying that i am unable to give the button command ( in the template page) to trigger in the viewmodel properly, help will be appreciated to figure this out, thanks.
I'd introduce a AddToCartCommand in your child control, which is invoked if the button is clicked.
// in your ListItemTemplate.xaml.cs
public static BindableProperty AddToCartCommandProperty = BindableProperty.Create(/* ... */);
public ICommand AddToCartCommand
{
get => (ICommand)GetValue(AddToCartCommandProperty);
set => SetValue(AddToCartCommandProperty, value);
}
public void Button_OnClick(object sender, EventArgs e)
{
this.AddToCartCommand?.Execute(null);
}
To bind to this from your parent view, you'll need a reference to that parent view with x:Name="Page" (or however you'd like to name your page) and then use a {Binding Source={x:Reference Page}, Path=BindingContext.Itemtapped}
<ContentPage ... x:Name="Page"> <!-- I have elided attributes that are not relevant to my answer -->
<!-- Elided -->
<ListView ...>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<templates:ListItemTemplate AddToCartCommand="{Binding Source={x:Reference Page}, Path=BindingContext.Itemtapped}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<!-- Elided -->
</ListView>
<!-- Elided -->
</ContentPage>

ListView Xamarin.forms dynamically load image from ItemSource with ImageResource

I have an issue in my xamarin.Forms application in MVVM. I have a ListView and I want to show an image in the ItemTemplate. To do that I have created a ImageRessource Class:
[ContentProperty("Source")]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Source == null)
{
return null;
}
var imageSource = ImageSource.FromResource($"CoPro.Assets.{Source}");
return imageSource;
}
}
Here is the xaml of my ListView:
<ListView ItemsSource="{Binding Series}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackLayout Orientation="Horizontal">
<Image Source="{images:ImageResource image.jpg}"/>
</StackLayout>
<Label Grid.Column="1" Text="{Binding Name}" Style="{StaticResource UsualLabelTemplate}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Besides, I have set "images" by this namespace :xmlns:images="clr-namespace:CoPro.Assets"
Like this, my ListView show the same picture for every "Series" Item.
For each Item there is a string property with the path of the image.
How can I show image of each Item, please?
Thanks in advance for your help.
I have just found the solution. I had to use Source property from Image tag:
<ListView Grid.Row="1" x:Name="MainListView" ItemsSource="{Binding Series}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding ImageUrl, Converter={StaticResource StringToImageSourceConverter}}"
Aspect="AspectFill"
/>
<Label Grid.Column="1" Text="{Binding Name}" Style="{StaticResource UsualLabelTemplate}" />
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The solution was to use a Converter, and Source property contained a string. This string is sent into parameter in my converter like this:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
return ImageSource.FromResource($"CoPro.Assets.{value}");
}
else
{
return null;
}
}
in this code "$"CoPro.Assets" is the path where is my image File.
Thanks for your help

Resources