Stephen York
Stephen York

Reputation: 1429

.NET Maui SwipeItem command binding to viewmodel ancestor fails

I have the following XAML

<CollectionView.ItemTemplate>
    <DataTemplate x:DataType="model:LogEntry">
        <SwipeView>
            <SwipeView.RightItems>
                <SwipeItem Text="Delete"
                           BackgroundColor="Orange"
                           Command="{Binding Source={RelativeSource AncestorType={x:Type viewModel:MainPageViewModel}}, Path=RemoveLogEntryCommand}"
                           CommandParameter="{Binding .}" />
                <SwipeItem Text="Delete" 
                           BackgroundColor="Red"
                           IsDestructive="True" />
            </SwipeView.RightItems>
            <Grid Padding="10">
                <Frame HeightRequest="125"
                           Padding="0"
                           Style="{StaticResource CardView}">
                    <Frame.GestureRecognizers>
                        <TapGestureRecognizer CommandParameter="{Binding .}"
                                              Command="{Binding Source={RelativeSource AncestorType={x:Type viewModel:MainPageViewModel}}, Path=GotoLogEntryDetailsCommand}" />
                    </Frame.GestureRecognizers>
                    <Grid Padding="0"
                              ColumnDefinitions="80,*">

with the following ICommand declarations using the community toolkit

[RelayCommand]
private async Task GotoLogEntryDetails(LogEntry logEntry)
{
    if (logEntry == null)
        return;

    await _appNavigationService.GoTo($"{nameof(LogEntryDetailsPage)}", true,
        new Dictionary<string, object>
        {
            { "LogEntry", logEntry }
        });


}

[RelayCommand]
private async Task RemoveLogEntry(LogEntry logEntry)
{

}

If I put a breakpoint in RemoveLogEntry then click my delete button the breakpoint is never reached. If I put RemoveLogEntry on the tap gesture and tap am item then the breakpoint is reached, so I know that the code generator has created a valid ICommand.

Intellisense tells me that the argument on CommandParameter . is actually a LogEntry therefore I need to declare the viewModel type.

What is wrong with the SwipeItem's ancestor binding path?

Upvotes: 3

Views: 2457

Answers (3)

Florian Redl
Florian Redl

Reputation: 28

I can confirm that the Command Attribute in SwipeItem does not work without a SwipeItems Object as Parent.

This Binding hase worked for me:

<SwipeItems>
<SwipeItem 
    IconImageSource="delete.png" 
    Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:TagListDetailViewModel}},Path=DeleteTagCommand}" 
    CommandParameter="{Binding .}"/>
</SwipeItems>

Upvotes: 1

Amine Seddiki
Amine Seddiki

Reputation: 11

A late answer to this. But I had the same issue with the ancestor method and I fixed it by surrounding the swipeItem with SwipeItems

 <SwipeView.RightItems>
   <SwipeItems>

    <SwipeItem Text="Delete"
                       BackgroundColor="Orange"
                       Command="{Binding Source={RelativeSource AncestorType={x:Type viewModel:MainPageViewModel}}, Path=RemoveLogEntryCommand}"
                       CommandParameter="{Binding .}" />
            <SwipeItem Text="Delete" 
                       BackgroundColor="Red"
                       IsDestructive="True" />
  </SwipeItems>
        </SwipeView.RightItems>

Upvotes: 1

ToolmakerSteve
ToolmakerSteve

Reputation: 21321

UPDATE

The first part of this answer ("Views vs Viewmodels") might be an incorrect explanation of the problem. I've seen this "viewmodel reference" syntax several places. Have not taken the time to test how/when it works. Regardless, referring to "View" ancestor always works. As does the "Alternative syntax" approach.


VIEWS vs VIEWMODELS

UI elements (views) and viewmodels are in DIFFERENT hierarchies. A viewmodel is NEVER an ancestor of a view. So you can't find such an ancestor.


VIEW Ancestor

Instead, find the VIEW that is the ancestor. That view's BindingContext will be the corresponding viewmodel.

Change:

<SwipeItem ... Command="{Binding Source={RelativeSource AncestorType={x:Type viewModel:MainPageViewModel}}, Path=RemoveLogEntryCommand}"

To:

<SwipeItem ... Command="{Binding Source={RelativeSource AncestorType={x:Type MainPageView}}, Path=BindingContext.RemoveLogEntryCommand}"

ALTERNATIVE SYNTAX

However, I never use that syntax. I find it easier to give an x:Name to the element I want to refer to. Then can use a simple x:Reference Source:

<ContentPage
  ...
  x:Name="thisPage">
    ...
    <SwipeItem ... Command={Binding BindingContext.RemoveLogEntryCommand, Source={x:Reference thisPage}"

Upvotes: 4

Related Questions