Search code examples
listviewxamarinxamarin.formsicommand

BindingContext of buttons within a separate ListView template xamarin


So, I'm developing applications on xamarin I try to include separate DataTemplate. My listView reference another ContentView whith templete for listview but the button don't call Icommand in View model.

sorry my english is not very well but I need help tks.

                  <ListView                     
                        ItemsSource="{Binding ItensOrder}"                                   
                        x:Name="PartListView"                          
                        HasUnevenRows ="True"
                        RowHeight="110"                  
                        IsPullToRefreshEnabled= "{Binding IsNotBusy}"          
                        CachingStrategy="RecycleElement"
                        IsVisible="{Binding IsNotBusy}"    
                        AbsoluteLayout.LayoutFlags="All" 
                        AbsoluteLayout.LayoutBounds="0,0,1,1">
                        <ListView.SeparatorColor>
                            <OnPlatform x:TypeArguments="Color" iOS="{StaticResource ListSeparator}" Android="Transparent"/>
                        </ListView.SeparatorColor>
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <ViewCell StyleId="disclosure">
                                    <local:BasketEquipamentPartCell/>
                                </ViewCell>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>

Template ( BasketEquipamentPartCell) code:

     <AbsoluteLayout HorizontalOptions="StartAndExpand">
                <Grid Padding="16" ColumnSpacing="16">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width=".5*"/>
                        <ColumnDefinition Width=".1*"/>
                        <ColumnDefinition Width=".1*"/>
                        <ColumnDefinition Width=".1*"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>

                    <Label VerticalOptions="Center"  Grid.Column="0" Grid.Row="0"  HorizontalOptions="StartAndExpand"  Text="Código Modelo:"/>
                    <Label VerticalOptions="Center" Grid.Column="0" Grid.Row="1"   HorizontalOptions="StartAndExpand" Text="Cópias Por Ciclo:"/>
                    <Label VerticalOptions="Center" Grid.Column="0" Grid.Row="2"   HorizontalOptions="StartAndExpand" Text="Descrição Produto:"/>
                    <Label VerticalOptions="Center" Grid.Column="0" Grid.Row="3"   HorizontalOptions="StartAndExpand" Text="Amount:"/>

                    <Label VerticalOptions="Center" Grid.Column="1" Grid.Row="0"  Grid.ColumnSpan="3" Text="{Binding EquipamentPart.EquipmentModel}"/>
                    <Label VerticalOptions="Center" Grid.Column="1" Grid.Row="1"  Grid.ColumnSpan="3"  Text="{Binding EquipamentPart.LifeCycle}"/>
                    <Label VerticalOptions="Center" Grid.Column="1" Grid.Row="2"  Grid.ColumnSpan="3" Text="{Binding EquipamentPart.Description}"/>
                    <Button Text="+"  Grid.Column="1" Grid.Row="3" 
                                BackgroundColor="DodgerBlue"
                                TextColor="White"
                                WidthRequest="100"
                                 />
                    <Label VerticalOptions="Center" Grid.Column="2" Grid.Row="3" Text="{Binding Amount}" HorizontalOptions="CenterAndExpand"/>
                    <Button Text="-"  Grid.Column="3" Grid.Row="3" 
                                BackgroundColor="DarkGreen"
                                Command="{Binding BindingContext.removeItemOder}"                                  
                                TextColor="White"
                                WidthRequest="100"                                   
                                 />
                    <Button Text="Delete" Grid.ColumnSpan="4" Grid.Column="0" Grid.Row="4" 
                                BackgroundColor="DarkRed"                                    
                                TextColor="White"
                                WidthRequest="100"
                                Command="{Binding Source={x:Reference BasketEquipamentPartCellPage}, Path=BindingContext.removeItemOder}" 
                                CommandParameter="{Binding .}"/>
                </Grid>
            </AbsoluteLayout>`

my command in viewmodel

      public class BasketEquipamentPartViewModel : ViewModelBase
{
    public ObservableCollection<OrderItem> ItensOrder { get; set; }

    INavigation navigation;
    private ICommand _removeItemOder, _addItemOder, _deleteOrder;

    private uint _badgeCount;
    public uint BadgeCount
    {
        get { return _badgeCount; }
        set
        {
            _badgeCount = value;
            OnPropertyChanged("BadgeCount");
        }
    }


    public ICommand removeItemOder =>
      _removeItemOder ?? (_removeItemOder = new Command<OrderItem>(async (item) => await removeItemOderCommandAsync(item)));
    public ICommand deleteOrder =>
      _deleteOrder ?? (_deleteOrder = new Command<OrderItem>(async (item) => await deleteOrderCommandAsync(item)));
    public ICommand addItemOder =>
      _addItemOder ?? (_addItemOder = new Command<OrderItem>(async (item) => await addItemOderCommandAsync(item)));


    public BasketEquipamentPartViewModel(INavigation navigation )
    {
        ItensOrder = new ObservableCollection<OrderItem>();            
        this.navigation = navigation;

        CreateMessaging();

    }
    public void CreateMessaging()
    {
        MessagingCenter.Unsubscribe<EquipamentPartOrderViewModel, EquipamentPart>(this, MessageKeys.AddProduct);
        MessagingCenter.Subscribe<EquipamentPartOrderViewModel, EquipamentPart>(this, MessageKeys.AddProduct, async (sender, arg) =>
        {
            BadgeCount++;

            await AddEquipamentPartAsync(arg);
        });
    }
    private async Task AddEquipamentPartAsync(EquipamentPart item)
    {

        if (ItensOrder.Where(x => x.EquipamentPart.Equals(item)).Count() > 0)
        {            
           var index= ItensOrder.IndexOf(ItensOrder.Where(c => c.EquipamentPart == item).FirstOrDefault());
            var i = new OrderItem(ItensOrder[index].EquipamentPart, ItensOrder[index].Amount+1);               

            ItensOrder.RemoveAt(index);
            ItensOrder.Insert(index,i);

        }
        else
        ItensOrder.Add(new OrderItem(item, 1));          
        OnPropertyChanged("ItensOrder");
    }

    private async Task addItemOderCommandAsync(OrderItem item)
    {
        if (ItensOrder.Where(x => x.EquipamentPart.Equals(item.EquipamentPart)).Count() > 0)
        {
            var index = ItensOrder.IndexOf(ItensOrder.Where(c => c.EquipamentPart == item.EquipamentPart).FirstOrDefault());
            var i = new OrderItem(ItensOrder[index].EquipamentPart, ItensOrder[index].Amount + 1);
            ItensOrder.RemoveAt(index);
            ItensOrder.Insert(index, i);
            BadgeCount++;
        }
    }
    private async Task removeItemOderCommandAsync(OrderItem item)
    {
        if (ItensOrder.Where(x => x.EquipamentPart.Equals(item.EquipamentPart)).Count() > 0)
        {
            var index = ItensOrder.IndexOf(ItensOrder.Where(c => c.EquipamentPart == item.EquipamentPart).FirstOrDefault());
            if (ItensOrder[index].Amount > 1)
            {
                var i = new OrderItem(ItensOrder[index].EquipamentPart, ItensOrder[index].Amount - 1);
                ItensOrder.RemoveAt(index);
                ItensOrder.Insert(index, i);
                BadgeCount--;
            }

        }
    }
    private async Task deleteOrderCommandAsync(OrderItem item)
    {
        if (ItensOrder.Where(x => x.EquipamentPart.Equals(item.EquipamentPart)).Count() > 0)
        {
            var index = ItensOrder.IndexOf(ItensOrder.Where(c => c.EquipamentPart == item.EquipamentPart).FirstOrDefault()); 
            ItensOrder.RemoveAt(index);
            BadgeCount-=item.Amount;
        }
    }
}

Solution

  • Each of your two views has its own BindingContext. The view which contains the ListView has a BindingContext of type BasketEquipamentPartViewModel, which is why the {Binding ItensOrder} picks up the correct collection for the ItemsSource.

    Then Xamarin will use BasketEquipamentPartCell multiple times, once for each element of the collection, setting BindingContext to each OrderItem. Thus the BasketEquipamentPartCell does not have any way to directly reference the BindingContext of the collection. When evaluating Command="{Binding BindingContext.removeItemOder}" on the Button, Xamarin.Forms will look through the properties of OrderItem for removeItemOder, but won't find it. When Xamarin.Forms fails to find a property, it silently ignores it, so the rest of your app works.

    To fix this, you'll need to define a Command property on OrderItem that can be bound to.

    Sketch of how to implement

    One way to approach this is to add a method on BasketEquipamentPartViewModel that listens for requests to remove items using the MessagingCenter (which you're already using for other things):

    public void CreateMessaging()
    {
        MessagingCenter.Subscribe<OrderItem>(this, "RemoveOrderItem", RemoveItem);
        // the rest of CreateMessaging you already have
    }
    
    private void RemoveItem(OrderItem itemToRemove)
    {
        // do the stuff you're already doing in removeItemOder
    }
    

    Note that it doesn't look like you're doing any async work in removeItemOder, so you should remove the async and make it return void.

    Then in the OrderItem class, create the command like:

    private ICommand _removeItemOrder;
    public ICommand removeItemOder => _removeItemOrder ??
           (_removeItemOrder = new Command<OrderItem>(item => MessagingCenter.Send(this, "RemoveOrderItem"));
    

    Then the {Binding BindingContext.removeItemOder} (or simply {Binding removeItemOder} - BindingContext is redundant here) will successfully find the command to invoke.