I have a page with a "CollectionView", the DataTemplate has a "Switch" for which I have added a "behaviors:EventToCommandBehavior" (from Xamarin.CommunityToolkit).
The project compiles and runs without error, but the command assigned to the Switch does not fire.
I have this in the Page XAML:
...
<CollectionView ...
...
<DataTemplate ...
...
<Switch x:Name="xamlSwitch"
Grid.Column="1"
IsToggled="{Binding IsActive, Mode=OneTime}"
OnColor="{StaticResource AppPrimaryColor}"
ThumbColor="{StaticResource SecondaryColor}"
VerticalOptions="Center">
<Switch.Behaviors>
<behaviors:EventToCommandBehavior
EventName="Toggled"
Command="{Binding ToggledCommand}"
CommandParameter="{Binding .}"
x:DataType="viewModels:DevicesPageViewModel"
/>
</Switch.Behaviors>
</Switch>
In the DevicesPageViewModel:
public Command ToggledCommand { get; set; }
public DevicesPageViewModel(){
ToggledCommand = new Command(async x => await ExecuteToggledCommand((UserDevice)x));
}
private async Task ExecuteToggledCommand(UserDevice x)
{
... code
}
I have tried (what feels like) a million combinations but nothing works! What am I doing wrong?
Edit: Full code for XAML and ViewModel
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:viewModels="clr-namespace:MyTest.ViewModels;assembly=MyTest"
xmlns:models="clr-namespace:MyTest.Models;assembly=MyTest"
xmlns:behaviors="http://xamarin.com/schemas/2020/toolkit"
x:Class="MyTest.Views.DevicesPage"
x:Name="pageName"
Title="My Devices">
<ContentPage.BindingContext>
<viewModels:DevicesPageViewModel />
</ContentPage.BindingContext>
<ContentPage.Content>
<AbsoluteLayout>
<StackLayout Margin="{StaticResource PageMargin}"
Spacing="80"
x:Name="MainLayout"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All" >
<RefreshView IsRefreshing="{Binding IsRefreshing}"
Command="{Binding RefreshItemsCommand}">
<CollectionView x:Name="ItemsCollectionView"
ItemsSource="{Binding Items}"
RemainingItemsThreshold="{Binding ItemThreshold}"
RemainingItemsThresholdReachedCommand="{Binding ItemThresholdReachedCommand}"
Margin="12,12,12,0"
>
<CollectionView.ItemTemplate>
<DataTemplate>
<!--SwipeView layout
see https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/swipeview#custom-swipe-items for commands -->
<SwipeView >
<SwipeView.RightItems>
<SwipeItems Mode="Reveal">
<SwipeItemView>
<Grid WidthRequest="85" Margin="0,16,0,16" RowSpacing="0" RowDefinitions=".5*,.5*" ColumnSpacing="0">
<BoxView Grid.RowSpan="2"
BackgroundColor="{StaticResource SwipeItemAlert}"
CornerRadius="0,8,0,8" />
<Image Grid.Row="0"
Margin="0,0,0,-6"
Aspect="AspectFit"
HorizontalOptions="Center"
Source="icon_white_alert"
HeightRequest="32"
VerticalOptions="End"/>
<Label Grid.Row="1"
Text="Report lost or stolen"
Padding="6,6,6,0"
FontAttributes="Bold"
FontSize="{StaticResource SwipeItemFontSize}"
HorizontalOptions="Center"
TextColor="White"
HorizontalTextAlignment="Center"
VerticalOptions="Start" />
</Grid>
</SwipeItemView>
<SwipeItemView>
<Grid WidthRequest="85" Margin="0,16,0,16" RowSpacing="0" RowDefinitions="*,*">
<BoxView Grid.RowSpan="2"
BackgroundColor="{StaticResource SwipeItemDefault }"
CornerRadius="0" />
<Image Grid.Row="0"
Margin="0,0,0,-6"
Aspect="AspectFit"
HorizontalOptions="Center"
Source="icon_white_more"
HeightRequest="32"
VerticalOptions="End"/>
<Label Grid.Row="1"
Text="More"
Padding="6,6,6,0"
FontAttributes="Bold"
FontSize="{StaticResource SwipeItemFontSize}"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
TextColor="White"
VerticalOptions="Start" />
</Grid>
</SwipeItemView>
</SwipeItems>
</SwipeView.RightItems>
<!--Content layout-->
<StackLayout Margin="0, 16, 0, 16" >
<Frame
Padding="{StaticResource SpacingMedium}"
CornerRadius="10"
IsClippedToBounds="True"
HorizontalOptions="Center"
VerticalOptions="Center"
HasShadow="True"
BackgroundColor="{StaticResource SecondaryColor}">
<Grid ColumnDefinitions="Auto, *" ColumnSpacing="{StaticResource DefaultSpacing}">
<Image Source="{Binding ProductImage}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60"
BackgroundColor="Aquamarine">
<Image.Clip>
<EllipseGeometry
Center="30,30"
RadiusX="30"
RadiusY="30"/>
</Image.Clip>
</Image>
<StackLayout Grid.Column="1" Padding="0">
<Label Text="{Binding ProductName}"
FontSize="18"
FontAttributes="Bold" />
<Label Text="{Binding MaskedPAN, StringFormat='ID: {0}'}"
FontAttributes="Bold" />
<Label Text="{Binding CardBalanceText}"
VerticalOptions="End" />
<Grid ColumnDefinitions="Auto, Auto" ColumnSpacing="6" >
<Label x:Name="switchStateLabel"
Text="Active"
VerticalOptions="Center" />
<Switch x:Name="xamlSwitch"
Grid.Column="1"
IsToggled="{Binding IsActive, Mode=OneTime}"
OnColor="{StaticResource AppPrimaryColor}"
ThumbColor="{StaticResource SecondaryColor}"
VerticalOptions="Center">
<Switch.Behaviors>
<behaviors:EventToCommandBehavior
EventName="Toggled"
Command="{Binding Path=BindingContext.ToggledCommand,
Source={x:Reference
Name=pageName}}"
CommandParameter="{Binding .}"
/>
</Switch.Behaviors>
</Switch>
</Grid>
</StackLayout>
</Grid>
</Frame>
</StackLayout>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
</StackLayout>
<StackLayout
x:Name="aiLayout"
IsVisible="{Binding IsBusy}"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
>
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<!--Semi transparent overlay panel-->
<BoxView BackgroundColor="White" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Opacity=".75" />
<Frame
CornerRadius="50"
HeightRequest="48"
WidthRequest="48"
Padding="5"
IsClippedToBounds="True"
HorizontalOptions="Center"
VerticalOptions="Center"
HasShadow="True"
BackgroundColor="{StaticResource SecondaryColor}">
<ActivityIndicator
x:Name="ai"
IsRunning="{Binding IsBusy}"
Color="{StaticResource AppPrimaryColor}"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
IsVisible="{Binding IsBusy}"
/>
</Frame>
</Grid>
</StackLayout>
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
ViewModel:
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using MyTestAppApi;
using MyTestAppApi.Models.ResponseData;
using MyTest.Services;
using Xamarin.Forms;
namespace MyTest.ViewModels
{
public class DevicesPageViewModel : BaseViewModel
{
#region Private Properties
private bool _isRefreshing;
private int _itemThreshold;
#endregion
#region Public Properties
public ObservableCollection<UserDevice> Items { get; set; }
public const string ScrollToPreviousLastItem = "Scroll_ToPrevious";
public bool IsRefreshing
{
get => _isRefreshing;
set => SetProperty(ref _isRefreshing, value);
}
public Command RefreshItemsCommand { get; set; }
public Command ItemThresholdReachedCommand { get; set; }
public Command LoadItemsCommand { get; set; }
public Command ToggledCommand { get; set; }
public int ItemThreshold
{
get => _itemThreshold;
set => SetProperty(ref _itemThreshold, value);
}
#endregion
#region Constructors
public DevicesPageViewModel()
{
ItemThreshold = 1;
Items = new ObservableCollection<UserDevice>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
ItemThresholdReachedCommand = new Command(async () => await ItemsThresholdReached());
RefreshItemsCommand = new Command(async () =>
{
IsRefreshing = false;
await ExecuteLoadItemsCommand();
});
ToggledCommand = new Command(async x => await ExecuteToggledCommand((UserDevice)x));
}
private async Task ExecuteToggledCommand(UserDevice x)
{
var myApi = new MyTestAppApiService(MyTestAppConfig.ApiBaseUrl);
var accessToken = await AccessToken();
myApi.UpdateDeviceStatus(accessToken, "?", "?");
}
#endregion
public async Task ItemsThresholdReached()
{
if (IsBusy)
return;
IsRefreshing = false;
IsBusy = true;
// Get next lot of data
try
{
var myApi = new MyTestAppApiService(MyTestAppConfig.ApiBaseUrl);
var accessToken = await AccessToken();
var rtnData = await myApi.GetUserDevicesAsync(accessToken, Items.Count);
foreach (var item in rtnData.Devices)
{
Items.Add(item);
}
if (!Items.Any())
{
ItemThreshold = -1;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
public async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsRefreshing = false;
IsBusy = true;
try
{
Items.Clear();
var myApi = new MyTestAppApiService(MyTestAppConfig.ApiBaseUrl);
var accessToken = await AccessToken();
var rtnData = await myApi.GetUserDevicesAsync(accessToken, 0);
foreach (var item in rtnData.Devices)
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
}
}