in my simple .NET MAUI MVVM project. Button Click event not hitting. Code is below
XAML
<......
xmlns:vm="clr-namespace:MVVMSqliteCrudNet8.ViewModels"
xmlns:models="clr-namespace:MVVMSqliteCrudNet8.Models"
x:DataType="vm:ProductsViewModel"
x:Class="MVVMSqliteCrudNet8.MainPage"
Title="MVVM SQlite Crud Net8">
<ContentPage.ToolbarItems>
<ToolbarItem Text="{OnPlatform Default='+ Add Product', iOS='+'}"
Command="{Binding SetOperatingProductCommand}"/>
</ContentPage.ToolbarItems>
<Grid RowDefinitions="Auto, *">
<VerticalStackLayout Grid.RowSpan="2"
VerticalOptions="Center"
HorizontalOptions="Center"
IsVisible="{Binding IsBusy}">
<ActivityIndicator IsRunning="True"
VerticalOptions="Center"
HorizontalOptions="Center"/>
<Label Text="{Binding BusyText}"
VerticalOptions="Center"
HorizontalOptions="Center"/>
</VerticalStackLayout>
<Label Grid.Row="0"
Text="Products"
FontAttributes="Bold"
FontSize="18"
Padding="10"/>
<Grid Grid.Row="1"
RowDefinitions="*, Auto">
<CollectionView Grid.Row="0"
ItemsSource="{Binding Products}">
<CollectionView.ItemsLayout>
<LinearItemsLayout ItemSpacing="10"
Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Product">
<Grid RowDefinitions="Auto, Auto"
ColumnDefinitions="*, Auto"
RowSpacing="5"
Padding="5"
BackgroundColor="#ECECEC">
<Label Grid.Row="0"
Grid.Column="0"
Text="{Binding Name}"
FontAttributes="Bold" />
<Label Grid.Row="1"
Grid.Column="0"
Text="{Binding Price, StringFormat='Price: {0}'}"
FontSize="12"
FontAttributes="Bold" />
<Button Grid.Row="0"
Grid.Column="1"
Text="Edit"
Padding="0"
HeightRequest="25"
FontSize="12"
CornerRadius="2"
Command="{Binding Source={RelativeSource AncestorType={x:Type vm:ProductsViewModel}}, Path=SetOperatingProductCommand}"
CommandParameter="{Binding .}"/>
<Button Grid.Row="1"
Grid.Column="1"
Text="Del"
Padding="0"
HeightRequest="25"
FontSize="12"
CornerRadius="2"
Command="{Binding Source={RelativeSource AncestorType={x:Type vm:ProductsViewModel}}, Path=DeleteProductCommand}"
CommandParameter="{Binding Id}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.EmptyView>
<ContentView>
<VerticalStackLayout HorizontalOptions="Center"
VerticalOptions="Center">
<Label Text="No Products Found"
FontSize="18"
FontAttributes="Bold"
HorizontalTextAlignment="Center"/>
<Label Text="Try creating a product from the form below"/>
</VerticalStackLayout>
</ContentView>
</CollectionView.EmptyView>
</CollectionView>
<VerticalStackLayout Grid.Row="1">
<BoxView Color="{DynamicResource Primary}"
HeightRequest="1"/>
<Grid RowDefinitions="Auto, Auto"
ColumnDefinitions="*, Auto"
Padding="10"
RowSpacing="10"
ColumnSpacing="10"
BackgroundColor="#CCBFFA">
<VerticalStackLayout Grid.Row="0"
Grid.Column="0">
<Label Text="Name"/>
<Entry Text="{Binding OperatingProduct.Name}"
Placeholder="Product name"
Margin="0"
BackgroundColor="#DAD1F9"/>
</VerticalStackLayout>
<VerticalStackLayout Grid.Row="0"
Grid.Column="1">
<Label Text="Price"/>
<Entry Text="{Binding OperatingProduct.Price}"
Placeholder="Product price"
Margin="0"
BackgroundColor="#DAD1F9"
Keyboard="Numeric"/>
</VerticalStackLayout>
<Button Grid.Row="1"
Grid.ColumnSpan="2"
Text="Update Product"
HorizontalOptions="Center"
VerticalOptions="End"
CornerRadius="4"
Padding="50, 0"
Command="{Binding SaveProductCommand}"
>
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding OperatingProduct.Id}"
Value="0">
<Setter Property="Text" Value="Create Product"/>
</DataTrigger>
</Button.Triggers>
</Button>
</Grid>
</VerticalStackLayout>
</Grid>
</Grid>
</ContentPage>
MVVM
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MVVMSqliteCrudNet8.Data;
using MVVMSqliteCrudNet8.Models;
namespace MVVMSqliteCrudNet8.ViewModels;
public partial class ProductsViewModel : ObservableObject
{
private readonly DatabaseContext _context;
public ProductsViewModel(DatabaseContext context)
{
_context = context;
}
[ObservableProperty]
private ObservableCollection<Product> _products = new();
[ObservableProperty]
private Product _operatingProduct = new();
[ObservableProperty]
private bool _isBusy;
[ObservableProperty]
private string _busyText;
public async Task LoadProductsAsync()
{
await ExecuteAsync(async () =>
{
var products = await _context.GetAll<Product>();
if (products is not null && products.Any())
{
Products ??= new ObservableCollection<Product>();
foreach (var product in products)
{
Products.Add(product);
}
}
}, "Fetching products...");
}
[RelayCommand]
private void SetOperatingProduct(Product? product) => OperatingProduct = product ?? new();
[RelayCommand]
private async Task SaveProductAsync()
{
if (OperatingProduct is null)
return;
var busyText = OperatingProduct.Id == 0 ? "Creating product..." : "Updating product...";
await ExecuteAsync(async () =>
{
if (OperatingProduct.Id == 0)
{
// Create product
await _context.AddItemAsync<Product>(OperatingProduct);
Products.Add(OperatingProduct);
}
else
{
// Update product
if (await _context.UpdateItemAsync<Product>(OperatingProduct))
{
var productCopy = OperatingProduct.Clone();
var index = Products.IndexOf(OperatingProduct);
Products.RemoveAt(index);
Products.Insert(index, productCopy);
}
else
{
await Shell.Current.DisplayAlert("Error", "Product updation error", "Ok");
return;
}
}
SetOperatingProductCommand.Execute(new());
}, busyText);
}
[RelayCommand]
private async Task DeleteProductAsync(int id)
{
await ExecuteAsync(async () =>
{
if (await _context.DeleteItemAsync<Product>(id))
{
var product = Products.FirstOrDefault(p => p.Id == id);
Products.Remove(product);
}
else
{
await Shell.Current.DisplayAlert("Delete Error", "Product was not deleted", "Ok");
}
}, "Deleting product...");
}
private async Task ExecuteAsync(Func<Task> operation, string? busyText = null)
{
IsBusy = true;
BusyText = busyText ?? "Processing...";
try
{
await operation?.Invoke();
}
catch (Exception ex)
{
string s = ex.Message;
}
finally
{
IsBusy = false;
BusyText = "Processing...";
}
}
}
Code Behind
using MVVMSqliteCrudNet8.ViewModels;
namespace MVVMSqliteCrudNet8
{
public partial class MainPage : ContentPage
{
ProductsViewModel viewModels;
public MainPage(ProductsViewModel _viewModels)
{
InitializeComponent();
BindingContext = viewModels;
viewModels = _viewModels;
}
protected override async void OnAppearing()
{
base.OnAppearing();
await viewModels.LoadProductsAsync();
}
}
}