Mohammed Ashraf

Mohammed Ashraf

  • 550
  • 2.3k
  • 502.4k

Button Click Event not hitting in Dotnet MAUI MVVM

Nov 9 2023 2:53 PM

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

}

 

 


Answers (1)