Chris
Chris

Reputation: 545

XAML, Binding, Source and Path

I am learning to understand, how the binding mechanism works in XAML for .NET MAUI. I am assuming this is the same for all XAML projects, WPF, MAUI etc.

At the end is the whole XAML.

This XAML works fine:

<Button WidthRequest="150" Text="Add Activity" 
                Command="{Binding AddActivityEntityCommand}"
                IsEnabled="{Binding IsNotBusy}"
                Grid.Row="2"
                Margin="8"/>

This on the other hand, doesn't just work as easy out of the box:

<Label HorizontalOptions="End" TextColor="Red" Padding="0,0,10,0" Text="🗑" 
       IsVisible="{Binding IsSynchronized}">
            <Label.GestureRecognizers>
                 <TapGestureRecognizer 
                       Command="{Binding Source={x:Type viewmodel:MainPageViewModel},
                                            Path=DeleteActivityCommand}" />
            </Label.GestureRecognizers>
 </Label>

System.Reflection.TargetException: Object does not match target type.

What am I missing?

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="OnesieMobile.View.MainPage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:model="clr-namespace:OnesieMobile.Model"
    xmlns:viewmodel="clr-namespace:OnesieMobile.ViewModel"
    x:DataType="viewmodel:MainPageViewModel"
    Title="{Binding Title}">
    <Grid
        ColumnDefinitions="*"
        RowDefinitions="20,50,50,*"
        RowSpacing="0">
        <Label HorizontalOptions="End" Margin="10,0,10,0"  Text="{Binding CurrentDateTime}" Grid.Row="0"/>
        <Entry Margin="10,0,10,0" 
            Grid.Row="1"  x:Name="entryNewActivity" 
            Placeholder="New Activityssss" HeightRequest="30" Text="{Binding NewActivityTitle}" />

        <Button WidthRequest="150" Text="Add Activity" 
                    Command="{Binding AddActivityEntityCommand}"
                    IsEnabled="{Binding IsNotBusy}"
                    Grid.Row="2"
                    Margin="8"/>

        <CollectionView
            Grid.Row="3"
                ItemsSource="{Binding ActivityEntities}"
                SelectionMode="None">
            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="model:ActivityEntity">
                    <Grid Padding="10,0,10,0">
                        <Frame Style="{StaticResource CardView}">
                            <Grid  ColumnDefinitions="*,30,50">
                                <StackLayout Padding="10,5,0,0" Grid.Column="0">
                                    <Label Text="{Binding Title}"  />
                                </StackLayout>
                                <StackLayout Padding="10,5,0,0" Grid.Column="1">
                                    <Label HorizontalOptions="End" TextColor="Red"
                                           Padding="0,0,10,0" Text="🗑" IsVisible="{Binding IsSynchronized}"  >
                                        <Label.GestureRecognizers>
                                            <TapGestureRecognizer
                                                Command="{Binding Source={x:Type viewmodel:MainPageViewModel},
                                                Path=DeleteActivityCommand}" />
                                        </Label.GestureRecognizers>
                                    </Label>
                                </StackLayout>
                                <StackLayout Padding="10,5,0,0" Grid.Column="2">
                                    <Label HorizontalOptions="End" 
                                           Padding="0,0,10,0" Text="✔" IsVisible="{Binding IsSynchronized}"  />
                                </StackLayout>
                            </Grid>
                        </Frame>
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
        <ActivityIndicator IsVisible="{Binding IsBusy}"
                               IsRunning="{Binding IsBusy}"
                               HorizontalOptions="FillAndExpand"
                               VerticalOptions="CenterAndExpand"
                               Grid.RowSpan="3"
                               Grid.ColumnSpan="2"/>

    </Grid>
</ContentPage>

Update:
MainPageViewModel.cs contains these Commands

[ICommand]
async Task DeleteActivityAsync()
{
}


[ICommand]
async Task AddActivityEntityAsync()
{
}

Upvotes: 1

Views: 3826

Answers (1)

ToolmakerSteve
ToolmakerSteve

Reputation: 21321

Inside an item template, there are several ways to refer to the original BindingContext. I like to do it by getting it from the collection itself:

<CollectionView x:Name="myCollection" ...>
    ...
    <CollectionView.ItemTemplate>
        ...
        Command="{Binding Source={x:Reference myCollection},
                  Path=BindingContext.DeleteActivityCommand}" />

FUTURE TBD:

I don't like the command name not existing anywhere in the implementing code. Hopefully there will eventually be a way to specify the command name in the ICommand attribute, to make it obvious:

// This won't work today.
[ICommand Name="DeleteActivityCommand"]
...

For now, we have to learn [ICommand]'s magic naming rules, which seem to be "Remove Async from end (if present); Add Command to end".

Upvotes: 5

Related Questions