L3prichaun13
L3prichaun13

Reputation: 1

In .NET MAUI with MVVM, How do you implement a SwipeView in a CollectionView

I have been at this for several days. Please show me my dumb error. I have even dumbed down my code to just about match the example in this article with no luck. (https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/collectionview/populate-data?view=net-maui-9.0) Example from Article;

<CollectionView x:Name="collectionView"
            ItemsSource="{Binding Monkeys}">
<CollectionView.ItemTemplate>
    <DataTemplate>
        <SwipeView>
            <SwipeView.LeftItems>
                <SwipeItems>
                    <SwipeItem Text="Favorite"
                               IconImageSource="favorite.png"
                               BackgroundColor="LightGreen"
                               Command="{Binding Source={x:Reference collectionView}, Path=BindingContext.FavoriteCommand}"
                               CommandParameter="{Binding}" />
                    <SwipeItem Text="Delete"
                               IconImageSource="delete.png"
                               BackgroundColor="LightPink"
                               Command="{Binding Source={x:Reference collectionView}, Path=BindingContext.DeleteCommand}"
                               CommandParameter="{Binding}" />
                </SwipeItems>
            </SwipeView.LeftItems>
            <Grid BackgroundColor="White"
                  Padding="10">
                <!-- Define item appearance -->
            </Grid>
        </SwipeView>
    </DataTemplate>
</CollectionView.ItemTemplate>

I am having multiple issues based upon what different method to solve my issue I try.. But ultimately my issue is that I cannot get the swipeview properly implemented where its command binds to the RelayCommand in my viewmodel and the commandparameter binds to the type of Model. No matter what I have tried, the swipeviews bindingcontext is ALWAYS of type Model.. I will try to provide as much relevant info as I can.

Below is my xaml page, not sure if all of it is helpful but I dont want to hinder your help.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:vm="clr-namespace:LoanShark.ViewModels"
         xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
         xmlns:custom="clr-namespace:LoanShark.Behaviors"
         xmlns:model="clr-namespace:LoanShark.Models"
         xmlns:repo="clr-namespace:LoanShark.Data.Repositories"
         xmlns:local="clr-namespace:LoanShark.Views"
         x:Class="LoanShark.Views.LoanPage"
         x:Name="Loan_Page"
         x:DataType="vm:LoanViewModel"
         Title="Loans">

<Grid>
    <!-- Main Content -->
    <!-- Show message when Loans is empty -->
    <Label Text="No loans found."
           IsVisible="{Binding Loans.Count, Converter={StaticResource IsZeroConverter}}"
           HorizontalOptions="Center"
           VerticalOptions="Center" />

     <!--Loan List-->
    <CollectionView x:Name="loansCollectionView"
                    ItemsSource="{Binding Loans}"
                    VerticalOptions="FillAndExpand">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="model:Loan">
                <SwipeView BindingContext="{Binding .}">
                    <SwipeView.LeftItems>
                        <SwipeItems>
                            <SwipeItem Text="Edit"
                                       BackgroundColor="LightGray"/>
                            <SwipeItem Text="Delete"
                                       BackgroundColor="LightPink"  
                                       Command="{Binding Source={x:Reference loansCollectionView}, Path=BindingContext.DeleteLoanCommand}"
                                       CommandParameter="{Binding .}" >                         
                                <SwipeItem.BindingContext>
                                    <model:Loan/>
                                </SwipeItem.BindingContext>
                            </SwipeItem>
                        </SwipeItems>
                    </SwipeView.LeftItems>

                    <Border x:Name="LoanBorder">
                        <Border.GestureRecognizers>
                            <TapGestureRecognizer Tapped="OnExpandButtonClicked"
                                                  NumberOfTapsRequired="1"/>
                        </Border.GestureRecognizers>
                        <Border.Behaviors>
                        <toolkit:TouchBehavior BindingContext="{Binding Path=BindingContext, Source={x:Reference LoanBorder}, x:DataType=Border}" 
                                               LongPressCommand="{Binding Source={x:Reference Loans}, Path=BindingContext.ShowContextMenuCommand, x:DataType=ContentPage}"
                                               LongPressCommandParameter="{Binding .}"
                                               LongPressDuration="1500"/>
                    </Border.Behaviors>
                        <VerticalStackLayout x:Name="LoansStackLayout" 
                                             x:DataType="model:Loan">

                            <!--Default View-->
                            <Label Text="{Binding Borrower.FullName}"
                                   Style="{DynamicResource SubHeaderLabelStyle}"
                                   FontSize="16" />

                            <!--Collapsible Details-->
                            <toolkit:Expander x:Name="LoanExpander"
                                              IsExpanded="{Binding IsExpanded, Mode=TwoWay}" 
                                              ExpandedChanged="OnExpanderToggled" >
                                <toolkit:Expander.Header>
                                    <Grid ColumnDefinitions="*, Auto" 
                                          VerticalOptions="CenterAndExpand">
                                        <Label Text="{Binding Name}"
                                               Style="{DynamicResource RegularLabelStyle}"
                                               FontSize="14"
                                               Grid.Column="0" />

                                         <!--Chevron Icon--> 
                                        <Image  x:Name="ChevronIcon"
                                                Source="up_chevron.svg"
                                                WidthRequest="24"
                                                HeightRequest="24"
                                                Grid.Column="1"
                                                RotationX="180"
                                                VerticalOptions="Center">
                                        </Image>

                                    </Grid>
                                </toolkit:Expander.Header>

                                <toolkit:Expander.Content>
                                    <Grid RowDefinitions="Auto, Auto, Auto, Auto" ColumnDefinitions="*, Auto">
                                        <!--Original Loan Amount-->
                                        <Label Text="Original:" Style="{DynamicResource SubHeaderLabelStyle}" Grid.Row="0" Grid.Column="0" />
                                        <Label Text="{Binding OriginalLoanAmount, StringFormat='{0:C}'}"
                                       Style="{DynamicResource RegularLabelStyle}" Grid.Row="0" Grid.Column="1" HorizontalOptions="End" />

                                        <!--Remaining Loan Amount-->
                                        <Label Text="Remaining:" Style="{DynamicResource SubHeaderLabelStyle}" Grid.Row="1" Grid.Column="0" />
                                        <Label Text="{Binding RemainingLoanAmount, StringFormat='{0:C}'}"
                                       Style="{DynamicResource RegularLabelStyle}" Grid.Row="1" Grid.Column="1" HorizontalOptions="End" />

                                        <!--Interest Rate-->
                                        <Label Text="Interest Rate:" Style="{DynamicResource SubHeaderLabelStyle}" Grid.Row="2" Grid.Column="0" />
                                        <Label Text="{Binding Interest, StringFormat='{0:P}'}"
                                       Style="{DynamicResource RegularLabelStyle}" Grid.Row="2" Grid.Column="1" HorizontalOptions="End" />

                                        <!--Loan End Date-->
                                        <Label Text="End Date:" Style="{DynamicResource SubHeaderLabelStyle}" Grid.Row="3" Grid.Column="0" />
                                        <Label Text="{Binding LoanEndDate, StringFormat='{0:MM/dd/yyyy}'}"
                                       Style="{DynamicResource RegularLabelStyle}" Grid.Row="3" Grid.Column="1" HorizontalOptions="End" />
                                    </Grid>
                                </toolkit:Expander.Content>
                            </toolkit:Expander>
                        </VerticalStackLayout>
                    </Border>
                </SwipeView>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>

    <!-- Floating Add Button -->
    <Button Text="+" 
            Command="{Binding NavigateToNewLoanCommand}"
            HorizontalOptions="End"
            VerticalOptions="End"
            Style="{DynamicResource FloatingActionButtonStyle}" />
</Grid>

I have exhausted all the options I can possibly come up with myself. I tried removing the x:Datatype model:Loan from the DataTemplate and putting it in the nested verticalstacklayout, that helped for the command binding but the commandparameter I could never get. Event with this option, I could not simply make the command "{Binding DeleteLoanCommand}" I still had to provide a somewhat complex binding of Command="{Binding Source={RelativeSource AncestorType={x:Type vm:LoanViewModel}}, Path=DeleteLoanCommand}"

Putting the x:DataType back on the DataTemplate makes the command never fire no matter how I structure the binding.

the viewmodel has a RelayCommand called DeleteLoanAsync which the communitytoolkit.mvvm converts to a command under the hood and renames it to DeleteLoanCommand

One last thing.... I will greatly appreciate any help on this, but also any resources or tips n tricks to gain a better understanding into bindings you think would help I will gladly read, I thought I knew and understood bindings but this one has me second guessing myself.

Upvotes: 0

Views: 108

Answers (2)

Liqun Shen-MSFT
Liqun Shen-MSFT

Reputation: 8220

Nothing wrong with your Bindings except some unnecessary settings.

You have already set the <DataTemplate x:DataType="model:Loan">, then you don't have to <SwipeItem.BindingContext> any more since the BindingContext is already type of model. And you use x:Reference markup which is great, then the SwipeCommand successfully binds to the RelayCommand in ViewModel.

Try the following code for the Command in ViewModel, which works on my side,

    [RelayCommand]
    public async Task DeleteLoanAsync(Loan loan)
    {
        Loan l = loan as Loan;
        Console.WriteLine("123");
    }

It can successfully retrieve the item of Loan type.

For more info, please refer to RelayCommand attribute

Upvotes: 0

Bhavanesh N
Bhavanesh N

Reputation: 1290

<SwipeItem.BindingContext>
<model:Loan/>
</SwipeItem.BindingContext>

This means you're setting BindingContext of your swipe view to new instance of Loan Model(BindingContext = new Loan();). Since you defined CommandParameter="{Binding .}" the newly created instance of the Loan model will be passed as parameter.

Remove the BindingContext of SwipeView, Then current Loan Item form you collection will be passed as parameter as you intend.

Upvotes: 0

Related Questions