1

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.

1 Answer 1

1

For your EventToCommandBehavior it appears you need access to:

  • ThisPopup typed as views:EditorPopup
  • ThisPopup.BindingContext type as vm:EventsEditorPopupViewModel
  • contentView.BindingContext (i.e. DataTemplate's BindingContext)

To do this, I would need to:

  • Give ContentView an x:Name, say "contentView"
  • Temporarily change the BindingContext of the EventToCommandBehavior to ThisPopup.BindingContext
  • Let the EventToCommandBehavior know that the new BindingContext is vm:EventsEditorPopupViewModel
  • Then we can do a simple Binding for the Command property
  • However, we have to do extra work now to fixup the CommandParameter binding since the BindingContext is no longer the DataTemplate so we use the ContentView.BindingContext to regain access to the DataTemplate's BindingContext.
<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" />
Sign up to request clarification or add additional context in comments.

1 Comment

That did the trick, thank you! Now off to apply this improved binding mojo elsewhere. Cheers!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.