I want to call a Command with a Parameter but the Command is in a different ViewModel to the Parameter

97 Views Asked by At

Using MVVM and Community Toolkit, I have a ContentPage (RentalPropertyHomePage) with a ViewModel (RentalPropertyHomePageVM), This ContentPage has a button bound to a RelayCommand and it has a ContentView (RentalPropertyListView) with it's ViewModel (RentalPropertyListVM). RentalPropertyHomePageVM has a RelayCommand which shows a PopUp. The ContentView (RentalPropertyListView) has CollectionView with the SelectedItem bound to an ObseveableProperty in it's ViewModel (RentalPropertyListVM). The DataTemplate for the CollectionView has a Button defined on it which binds to the RelayCommand in the VM of the parent Page (RentalPropertyHomePageVM) and happily sends the command the current item as the Parameter. I want to send that SelectedItem as the Parameter when I click the button which is defined on the parent Page.

Somewhat simplified XAML ...

<ContentPage x:Class="PropertyManagement.RentalProperties.ContentPages.RentalPropertyHomePage" >
        <VerticalStackLayout
            <Button Command="{Binding ShowEditPropertyCommand}" CommandParameter="{Binding .How do I get the selectedItem from the collectionview??}" Text="Edit" />
            <content:RentalPropertyListView />
        </VerticalStackLayout>
</ContentPage>

Relevant ViewModel code

    [RelayCommand]
    private async Task ShowEditProperty(RentalPropertyDetail rentalPropertyDetail)
    {
        // is a property selected in the list?
        if (rentalPropertyDetail != null)
        {
            popupEdit = new(); // create new popup each time
            var result = await Shell.Current.CurrentPage.ShowPopupAsync(popupEdit);

            if (result is int theResult)
                await showToast(theResult);
        }
    }
<ContentView x:Class="PropertyManagement.RentalProperties.ContentViews.RentalPropertyListView" >

<CollectionView HorizontalOptions="Center" ItemsSource="{Binding ObservableCollectionOfRentalProperties}" 
     SelectionMode="Single" SelectedItem="{Binding SelectedProperty}">
        <CollectionView.ItemTemplate>
           <DataTemplate x:DataType="models:RentalPropertyDetail">
               <Grid ColumnDefinitions="*, 125" >                                
                   <Label Grid.Column="0" Text="{Binding Address}" />
                   <Label Grid.Column="1" Text="{Binding PurchaseCost, StringFormat='{0:C0}'}" />
                   <Button Grid.Column="3" ImageSource="pencil.png"
                       Command="{Binding Source={RelativeSource  AncestorType={x:Type ContentPage}}, Path=BindingContext.ShowEditPropertyCommand }" CommandParameter="{Binding .}" />
           </DataTemplate>
        </CollectionView.ItemTemplate>
</CollectionView>
</ContentView>

Code Behind

RentalPropertyListViewModel vm = new RentalPropertyListViewModel();
    public RentalPropertyListView()
    {
        InitializeComponent();
        BindingContext = vm;
    }

Relevant VM code

    [ObservableProperty]
    private ObservableCollection<RentalPropertyDetail> _observableCollectionOfRentalProperties;

    [ObservableProperty]
    RentalPropertyDetail selectedProperty;

How do I pass a selected item from a collection view into a command that is in its parent page? It appears that it can be done using messaging and sending objects around but that doesn't seem the best solution, not to me anyway.

1

There are 1 best solutions below

0
InquisitorJax On

Ideally, they should share a ViewModel - but if you absolutely need RentalPropertyListView to be self-contained, other than the option mentioned of messaging, you could consider adding a bindable OneWayToSource SelectedListItem property to RentalPropertyListView - so it propagates that value to the parent VM every time it's updated from within the view.