2
votes

In my project, I have two ListView in different ViewModels whit same ViewCell content. I extract this ViewCell in other XAML file to reuse in ListView like this:

    <views:MvxContentPage.Content>
    <ScrollView x:Name="scrollList">
        <StackLayout x:Name="Root">
                    <!-- ... -->

            <repeater:RepeaterView x:Name="MainList" ShowSeparator="False"
                                   SelectedItemCommand="{Binding SelectedCommand}"
                                   IsVisible="True"
                                   ItemsSource="{Binding Items}">
                <repeater:RepeaterView.ItemTemplate>
                    <DataTemplate>
                        <local:ItemList FavoriteCommand="Binding path=FavCommand, Source={x:Reference MainList}}"
                                                        FavoriteCommandParameter="{Binding .}"/>
                    </DataTemplate>
                </repeater:RepeaterView.ItemTemplate>

            </repeater:RepeaterView>

                     <!-- ... -->

        </StackLayout>
    </ScrollView>
</views:MvxContentPage.Content>
...

This works perfectly to show data but when binding command in the label, it's not working.

<views:MvxViewCell
xmlns="http://xamarin.com/schemas/2014/forms"
x:TypeArguments="viewModels:ItemListViewModel"
xmlns:viewModels="clr-namespace:Template.Core.ViewModels;assembly=Template.Core"

                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                  x:Class="Template.Core.Views.ItemList"
                  xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
                  xmlns:iconize="clr-namespace:Plugin.Iconize;assembly=Plugin.Iconize"
                  xmlns:Helpers="clr-namespace:Template.Core.Helpers">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*" />
            <ColumnDefinition Width="6*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Image Source="ukflag"
               Grid.Row="1"
               Grid.RowSpan="4"
               WidthRequest="50"
               HeightRequest="80"
               Grid.Column="0" />
        <Label Text="{Binding Nombre}"
               Grid.Row="1"
               Grid.Column="1"
               FontAttributes="Bold"
               FontSize="15" />
        <Label Text="{Binding Direcciones[0].toString}"
               Grid.Row="2"
               Grid.Column="1"
               FontSize="11" />
        <iconize:IconLabel Text="fas-heart"
                           BackgroundColor="Transparent"
                           Grid.Row="1"
                           Grid.Column="2"
                           FontSize="35"
                           Grid.RowSpan="2"
                           VerticalOptions="Center">
            <iconize:IconLabel.GestureRecognizers>
                <TapGestureRecognizer Command="{Binding FavCommand}"
                                      CommandParameter="{Binding .}" />
            </iconize:IconLabel.GestureRecognizers>
        </iconize:IconLabel>
        <StackLayout Orientation="Horizontal"
                     Grid.Row="3"
                     Grid.Column="1">
            <iconize:IconLabel Text="fas-map-marker-alt"
                               TextColor="Black"
                               FontSize="15" />
            <Label Text="{Binding Distancia}"
                   FontSize="13" />
        </StackLayout>
    </Grid>
</views:MvxViewCell>



public partial class ItemList

{

    public ItemList()
    {
        InitializeComponent();

    }
}

Before to separate ViewCell in the file, binding works correctly but now not call ViewModel command. My idea is binding two commands from the view where it is. How can it be done? Many thanks!!

1
Can you share the whole ViewCell's source code including code behind (I guess it is the ItemList element)? Looks like the BindingContext is not being set properlyDiego Rafael Souza
Hello! I just edited the code to show everything. Thank you very much!pablogupi
Humn... You're using MvvmCross, I don't know how it works or if it is interfering on the behavior, but check out my answer, see if it works for you.Diego Rafael Souza

1 Answers

3
votes

I don't know how is your proficiency in Xamarin Forms, but definitely it isn't a beginner's code. I think it was just a misunderstanding. The issue you've described looks like a binding issue, as I've commented.

So, the cake recipe is:

  1. Expose in the custom view cell those properties you want to be able to use externaly;
  2. Bind the internal view cell properties into the elements as you want;
  3. Use the custom view cell with external binds.

The step by step walkthrough:

1 - Exposing properties enabling external bindings

I believe you I'll get the expected result just adding the properties you want to be bound outside like bindable properties:

// viewCell's code-behind
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ItemListCell : ContentView
{
    public static BindableProperty NameProperty = BindableProperty.Create(nameof(Name), typeof(string), typeof(ItemListCell));
    public string Name
    {
        get => (string)GetValue(NameProperty);
        set => SetValue(NameProperty, value);
    }

    public static BindableProperty FavoriteCommandProperty = BindableProperty.Create(nameof(FavoriteCommand), typeof(ICommand), typeof(ItemListCell));
    public ICommand FavoriteCommand
    {
        get => (ICommand)GetValue(FavoriteCommandProperty);
        set => SetValue(FavoriteCommandProperty, value);
    }

    public static BindableProperty FavoriteCommandParameterProperty = BindableProperty.Create(nameof(FavoriteCommandParameter), typeof(object), typeof(ItemListCell));
    public object FavoriteCommandParameter
    {
        get => GetValue(FavoriteCommandParameterProperty);
        set => SetValue(FavoriteCommandParameterProperty, value);
    }

    public static BindableProperty DistanceProperty = BindableProperty.Create(nameof(Distance), typeof(string), typeof(ItemListCell));
    public string Distance
    {
        get => (string)GetValue(DistanceProperty);
        set => SetValue(DistanceProperty, value);
    }

    public ItemListCell ()
    {
        InitializeComponent ();
    }
}

2 - Using properties on [view-cell's] internal bindings

On your ViewCell xaml you should bind the internal elements properties to a known binding context (in this case I made it be called This). See the examples on Label and TapGestureRecognizer:

// viewCell's XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="App2.ItemListCell"
             x:Name="This">
  <ContentView.Content>
        <Grid BindingContext="{x:Reference This}">
            <!-- Keep all the viewcell xaml source code unmodified -->
            <Label Text="{Binding Name}"/>
            <!-- ... -->
            <iconize:IconLabel.GestureRecognizers>
                <TapGestureRecognizer Command="{Binding FavoriteCommand}"
                                      CommandParameter="{Binding FavoriteCommandParameter}" />
            </iconize:IconLabel.GestureRecognizers>
            <!-- ... -->
            <!-- Keep all the viewcell xaml source code unmodified -->
        </Grid>
    </ContentView.Content>
</ContentView>

Notice the Grid's BindingContext set and the view cell's binding to internal properties which are exposed to external bindings

3 - Using the custom view cell

Here you will use your custom view cell and bind with the item list object. Then it should works fine. It should be like this:

...
<ListView ...>
    <ListView.ItemTemplate>
        <DataTemplate>
            <local:ItemListCell Name="{Binding Nombre}"
                                FavoriteCommand="{Binding FavCommand}"
                                FavoriteCommandParameter="{Binding .}"/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
...

Updating: In case you are using a single implementation of FavCommand on your page's view model:

<ContentPage ...
             x:Name="PageInstance">
    ...
    <ListView ...>
        <ListView.ItemTemplate>
            <DataTemplate>
                <local:ItemListCell Name="{Binding Nombre}"
                                    FavoriteCommand="{Binding BindingContext.FavCommand, Source={x:Reference PageInstance}}"
                                    FavoriteCommandParameter="{Binding .}"
                                    .../>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    ...
</ContentPage>

I hope it helps.