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(); } }
}