maui

EventToCommandBehavior in CollectionView item template


I'm fighting with bindings in a CollectionView, enough time wasted!

The data model class is called Event. An ObservableCollection of these is bound to the CV. I am trying hard to stick to MVVM so I want to use EventToCommandBehavior to keep everything in the view model.

Here is the xaml and VM code:

<?xml version="1.0" encoding="utf-8" ?>
<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
               xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
               xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
               xmlns:local="clr-namespace:My.Namespace.MyApp"
               xmlns:models="clr-namespace:My.Namespace.MyApp.Models"
               xmlns:views="clr-namespace:My.Namespace.MyApp.Views"
               xmlns:vm="clr-namespace:My.Namespace.MyApp.ViewModels"
               x:Class="views:EditorPopup"
               x:DataType="vm:EditorPopupViewModel"
               x:Name="ThisPopup">
    <CollectionView ItemsSource="{Binding DataProvider.Events}"
                                SelectionMode="None">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="models:Event">
                <ContentView>
                    <Grid>

                        <Switch IsToggled="{Binding IsEnabled}">
                            <Switch.Behaviors>
                                <toolkit:EventToCommandBehavior
                                    EventName="Toggled"
                                    BindingContext="{Binding Source={x:Reference ThisPopup}}"
                                    Command="{Binding BindingContext.ToggleEventCommand, Source={x:Reference ThisPopup}, x:DataType=vm:EventsEditorPopupViewModel}"
                                    CommandParameter="{Binding}" />
                            </Switch.Behaviors>
                        </Switch>

                    </Grid>
                </ContentView>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</toolkit:Popup>
    [RelayCommand]
    private async Task ToggleEventAsync(Event eventData)
    {
        if (eventData != null 
            && GetEventById(eventData.Id) is Event e)
        {
            await DataProvider.ToggleAsync(e.Id);
            await DataProvider.SaveAsync();
        }
    }

I am getting these binding failures in VS:

  • Mismatch between the specified x:DataType (My.Namespace.MyApp.Models.Event) and the current binding context (My.Namespace.MyApp.Views.EditorPopup).

  • Mismatch between the specified x:DataType (My.Namespace.MyApp.ViewModels.EditorPopupViewModel) and the current binding context (My.Namespace.MyApp.Views.EditorPopup).

My intention is to get the VM from the popup (ThisPopup), so that I can then reference the Command method, and pass the bound item as the parameter.


Solution

  • For your EventToCommandBehavior it appears you need access to:

    To do this, I would need to:

    <ContentView x:Name="contentView">
        <Grid>
            <Switch IsToggled="{Binding IsEnabled}">
                <Switch.Behaviors>
                    <toolkit:EventToCommandBehavior
                        EventName="Toggled"
                        BindingContext="{Binding BindingContext, Source={Reference ThisPopup}, x:DataType='views:EditorPopup'}"
                        x:DataType="vm:EventsEditorPopupViewModel"
                        Command="{Binding ToggleEventCommand}"
                        CommandParameter="{Binding BindingContext, Source={Reference contentView}, x:DataType='ContentView'}" />
    

    Generally the rules I follow are:

    1. Specify an inline x:DataType for every Binding to Source, e.g.
    "{Binding Path=anything, Source={Reference xyz}, x:DataType='type_of_xyz'}"
    
    1. Specify a new x:DataType for every BindingContext change, e.g.
    <AnyComponent
         BindingContext="change_to_anything_here"
         x:DataType="type_of_BindingContext" />