Search code examples
c#xamldata-bindingmaui

Data Binding for Flyout page "'No property, BindableProperty, or event found for "Command", or mismatching type between value and property."


I'm developing a mobile app using .Net MAUI and having trouble with data binding in XAML. I have MainPage.xaml that receives data from MainPageViewModel.cs which inherits from ObservableObject.

In Flyout.cs, I have Title, IconSource, and Command and get them in <DataTemplate> in Mainpage.xaml. I pass values to each of the property in <CollectionView.ItemSource> and only to Command, I am passing a string of method that is defined in MainPageViewModel.xaml.cs.

I am confused about how to perform data binding here. Values for Title and IconSource are bound correctly and visible, but only Command is not working.

How can I get this to work?

MainPage.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<FlyoutPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:vm="clr-namespace:Realizer.ViewModels"
            xmlns:models="clr-namespace:Realizer.Models"
            xmlns:local="clr-namespace:Realizer.Pages"
            x:Class="Realizer.Pages.MainPage"
            x:DataType="vm:MainPageViewModel"
            Title="Home">

    <FlyoutPage.Flyout>
        <ContentPage Title="Home page" BackgroundColor="LightGrey">
            <VerticalStackLayout>
                <CollectionView>
                    <CollectionView.ItemsSource>
                        <x:Array Type="{x:Type models:Flyout}">
                            <models:Flyout Title="Home"
                                       IconSource="home.png"
                                       Command="{Binding GoHomeCommand}"/>
                            <models:Flyout Title="Clients"
                                        IconSource="person.png"
                                        Command="{Binding GoToClientsPageCommand}"/>
                            <models:Flyout Title="Products"
                                        IconSource="medicine_bag.png"
                                       Command="{Binding GotoProductsPageCommand}"/>
                        </x:Array>
                    </CollectionView.ItemsSource>
                    <CollectionView.ItemTemplate>
                        <DataTemplate>
                            <Grid Padding="20, 5">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="30"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Image Source="{Binding IconSource, Source={RelativeSource AncestorType={x:Type models:Flyout}}}"
                                   Scale="1.5"/>
                                <Button Grid.Column="1"
                                    Margin="5"
                                    Text="{Binding Title, Source={RelativeSource AncestorType={x:Type models:Flyout}}}"
                                    Command="{Binding Command,Source={RelativeSource AncestorType={x:Type models:Flyout}}}"
                                    FontSize="16"
                                    FontAttributes="Bold"
                                    TextColor="Black"
                                    HeightRequest="60"
                                    Padding="0,0,110,0"
                                    BackgroundColor="ForestGreen"/>
                            </Grid>
                        </DataTemplate>
                    </CollectionView.ItemTemplate>
                </CollectionView>
                <Label Text="this is it"
                       Margin="20"/>
            </VerticalStackLayout>
        </ContentPage>
    </FlyoutPage.Flyout>

    <FlyoutPage.Detail>
        <NavigationPage BarBackgroundColor="Navy"
                        BarTextColor="Black"
                        Title="Home">
            <x:Arguments>
                <ContentPage Title="Home" BackgroundColor="White">
                    <VerticalStackLayout>
                        <Label Text="this is detail"
                               Margin="20"/>
                    </VerticalStackLayout>
                </ContentPage>

            </x:Arguments>
        </NavigationPage>
    </FlyoutPage.Detail>

</FlyoutPage>

MainPage.xaml.cs:

using Realizer.ViewModels;

namespace Realizer.Pages;

public partial class MainPage : FlyoutPage
{
    public MainPage(MainPageViewModel viewModel) {
        InitializeComponent();
        BindingContext = viewModel;
    }
}

MainPageViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace Realizer.ViewModels
{
    public partial class MainPageViewModel : ObservableObject
    {
        public MainPageViewModel()
        { 
        }

        [RelayCommand]
        private void GoToClientsPage()
        {
            Console.WriteLine("hi");
        }

        [RelayCommand]
        private void GoHome()
        {
            Console.WriteLine("hi home");
        }

        [RelayCommand]
        private void GoToProductsPage()
        {
            Console.WriteLine("hi pro");
        }
    }
}

Flyout.cs

namespace Realizer.Models
{
    internal class Flyout
    {
        public string Title { get; set; }
        public string IconSource { get; set; }
        public string Command { get; set; }     
        }
}

MauiProgram.cs

            builder.Services.AddSingleton<MainPage>();
            builder.Services.AddSingleton<MainPageViewModel>();

I referred to this tutorial when writing the code.

I tried Command="{Binding GoHomeCommand, Source={RelativeSource AncestorType={x:Type vm:MainPageViewModel}}}}"/>, but I am not even sure what I'm doing here.


Solution

  • Instead of figuring out the data type, I made a "OnClick" method in "MainPageViewModel.cs", called it in <Button> in <DataTemplate> in "MainPage.xaml", and deleted each "Command" property in <model:flyout>.

    MainPageViewModel.cs:

      [RelayCommand]
      private async void OnClick(string page){
        if(page == "Home") ...//page navigation here
        else if (page == "Clients")...
    
      }
    

    MainPage.xaml:

    <DataTemplate>
              ...
           <Button Grid.Column="1"
                   Margin="5"
                   Text="{Binding Title, Source={RelativeSource AncestorType={x:Type models:Flyout}}}"
                   Command="{Binding ClickCommand, Source={RelativeSource AncestorType={x:Type vm:MainPageViewModel}}}"
                   CommandParameter="{Binding Title, Source={RelativeSource AncestorType={x:Type models:Flyout}}}"/>
             ...
    </DataTemplate>