How to bind index of item in nested BindableLayout.ItemsSource to CommandParameter - Xamarin

和自甴很熟 提交于 2021-01-07 06:58:08

问题


I'm trying to figure out how you can get the index from a list inside XAML.

Context

A product has multiple specification categories/groups which contain the specification details.

  • Product & Ingrediënten are the specification groups.
  • Land van afkomst : Nederland are the specs details

In the XAML code, I'm using a nested list. The application needs to pass the Index so the users can delete and add specifications correctly.

The index at Binding Source="0" /> & CommandParameter="0" needs to be passed instead of "0".

<StackLayout BindableLayout.ItemsSource="{Binding Product.Specifications}">
    <BindableLayout.ItemTemplate>
        <DataTemplate>
            <StackLayout>
                <Entry Text="{Binding Title, Mode=TwoWay}" />
                <StackLayout BindableLayout.ItemsSource="{Binding SpecificationDetails}">
                    <BindableLayout.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width=".44*" />
                                    <ColumnDefinition Width=".44*" />
                                    <ColumnDefinition Width=".12*" />
                                </Grid.ColumnDefinitions>
                                <Entry Grid.Row="0"
                                       Grid.Column="0"
                                       Text="{Binding Title}"
                                       Style="{StaticResource spec-entry-style}" />
                                <Entry Grid.Row="0"
                                       Grid.Column="1"
                                       Text="{Binding Description}"
                                       Style="{StaticResource spec-entry-style}" />
                                <!-- Delete specification detail -->
                                <Button Grid.Column="2"
                                        Text="X"
                                        Style="{StaticResource cancel-button-style}"
                                        Command="{Binding Path=BindingContext.DeleteSpecificationEntryCommand, Source={x:Reference Page}}">
                                    <Button.CommandParameter>
                                        <MultiBinding Converter="{StaticResource SpecsConverter}">
                                            <Binding Source="0" />
                                            <Binding Path="." />
                                        </MultiBinding>
                                    </Button.CommandParameter>
                                </Button>
                            </Grid>
                        </DataTemplate>
                    </BindableLayout.ItemTemplate>
                </StackLayout>
                <!-- Add specification detail -->
                <Button Text="Voeg specificatie toe"
                        Command="{Binding AddSpecicifationEntriesCommand}"
                        CommandParameter="0"
                        HorizontalOptions="Start" />
            </StackLayout>
        </DataTemplate>
    </BindableLayout.ItemTemplate>
</StackLayout>

<!-- Add Specification group -->
<Button Text="Voeg nieuwe specificatie toe"
        Command="{Binding AddNewSpecificationGroupCommand}" />

The specs group model:

public class SpecificationDbViewModel : INotifyPropertyChanged
{
    private int _id;
    private string _title;
    private ObservableCollection<SpecificationDetailDbViewModel> _specificationDetails;

    public int Id
    {
        get => _id;
        set 
        { 
            _id = value;
            RaisePropertyChanged(nameof(Id));
        }
    }

    public string Title
    {
        get => _title;
        set
        {
            _title = value;
            RaisePropertyChanged(nameof(Title));
        }
    }

    public ObservableCollection<SpecificationDetailDbViewModel> SpecificationDetails
    {
        get => _specificationDetails;
        set
        {
            _specificationDetails = value;
            RaisePropertyChanged(nameof(Title));
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

The specs detail model:

public class SpecificationDetailDbViewModel : INotifyPropertyChanged
{
    private int _id;
    private string _title;
    private string _description;

    public int Id
    {
        get => _id;
        set
        {
            _id = value;
            RaisePropertyChanged(nameof(Id));
        }
    }

    public string Title
    {
        get => _title;
        set
        {
            _title = value;
            RaisePropertyChanged(nameof(Title));
        }
    }
    public string Description
    {
        get => _description;
        set
        {
            _description = value;
            RaisePropertyChanged(nameof(Description));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

I'm using a MultiBinder converter to pass multiple values to the command. 1 method inside the ViewModel that removes the specifications:

private void ExecuteDeleteSpecificationEntryCommand(SpecificationDetailWithIndex specificationDetailWithIndex)
{
    Product.Specifications[specificationDetailWithIndex.Index].SpecificationDetails.Remove(specificationDetailWithIndex.SpecificationDetailDbViewModel);
}

回答1:


  1. Don't bother your index, just can send THE object back as a parameter of the command.
Command="{...}"  //same binding
CommandParameter="{Binding .}"  //new line
  1. And define your command with the correct parameter in your ViewModel.
    public ICommand<SpecificationDetailDbViewModel> DeleteSpecificationEntryCommand => new Command<SpecificationDetailDbViewModel>(ExecuteDeleteSpecificationEntryCommand);

    private void ExecuteDeleteSpecificationDetailEntryCommand(SpecificationDetailDbViewModel item)
    {
        //remvoe item from collection
        Product.Specifications?.Remove(item);
    }

And you can also use groups in the list view btw.




回答2:


Since it seems very hard to get an Index inside the XAML I created a different solution. I still hope someone knows how to get Index inside nested stacklayout lists. My teacher says it is bad practice to loop over things when not needed for performance.

    <StackLayout BindableLayout.ItemsSource="{Binding Product.Specifications}">
    <BindableLayout.ItemTemplate>
        <DataTemplate>
            <StackLayout>
                <Entry Text="{Binding Title, Mode=TwoWay}" />
                <StackLayout BindableLayout.ItemsSource="{Binding SpecificationDetails}">
                    <BindableLayout.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width=".44*" />
                                    <ColumnDefinition Width=".44*" />
                                    <ColumnDefinition Width=".12*" />
                                </Grid.ColumnDefinitions>
                                <Entry Grid.Row="0"
                                       Grid.Column="0"
                                       Text="{Binding Title}"
                                       Style="{StaticResource spec-entry-style}" />
                                <Entry Grid.Row="0"
                                       Grid.Column="1"
                                       Text="{Binding Description}"
                                       Style="{StaticResource spec-entry-style}" />
                                <!-- Delete specification detail -->
                                <Button Grid.Column="2"
                                        Text="X"
                                        Style="{StaticResource cancel-button-style}"
                                        Command="{Binding Path=BindingContext.DeleteSpecificationDetailEntryCommand, Source={x:Reference Page}}" 
                                        CommandParameter="{Binding .}"/>
                            </Grid>
                        </DataTemplate>
                    </BindableLayout.ItemTemplate>
                </StackLayout>
                <!-- Add specification detail -->
                <Button Text="Voeg specificatie toe"
                        Command="{Binding Path=BindingContext.AddSpecicifationDetailEntryCommand, Source={x:Reference Page}}"
                        CommandParameter="{Binding .}" 
                        HorizontalOptions="Start" />
                <Button Text="{Binding Title, StringFormat='Verwijder {0}'}"
                                        Style="{StaticResource cancel-button-style}"
                                        FontSize="12"
                                        Command="{Binding Path=BindingContext.DeleteSpecificationGroupEntryCommand, Source={x:Reference Page}}"
                                        CommandParameter="{Binding .}"
                                        HorizontalOptions="Start" />
            </StackLayout>
        </DataTemplate>
    </BindableLayout.ItemTemplate>
</StackLayout>

<!-- Add Specification group -->
<Button Text="Voeg nieuwe specificatie toe"
        Command="{Binding AddSpecicifationGroupEntriesCommand}" />

Methods for adding and removing specs

 private void ExecuteAddSpecicifationGroupEntriesCommand()
    {
        Product.Specifications.Add(new SpecificationDbViewModel()
        {
            SpecificationDetails = new ObservableCollection<SpecificationDetailDbViewModel>()
                    {
                        new SpecificationDetailDbViewModel()
                    }
        });
    }
    

    private void ExecuteDeleteSpecicifationGroupEntriesCommand(SpecificationDbViewModel specsViewModel)
    {
        Product.Specifications.Remove(specsViewModel);
    }

    private void ExecuteAddSpecicifationDetailEntryCommand(SpecificationDbViewModel specsViewModel)
    {
        Product.Specifications[Product.Specifications.IndexOf(specsViewModel)].SpecificationDetails.Add(new SpecificationDetailDbViewModel());
    }

    private void ExecuteDeleteSpecificationDetailEntryCommand(SpecificationDetailDbViewModel specsDetailViewModel)
    {
        for(int i = 0; i < Product.Specifications.Count; i++)
        {
            if(Product.Specifications[i].SpecificationDetails.IndexOf(specsDetailViewModel) != -1)
            {
                Product.Specifications[i].SpecificationDetails.Remove(specsDetailViewModel);
                return;
            }
        }
    }

Create the commands inside the constructor of your viewmodel

        AddSpecicifationDetailEntryCommand = new Command<SpecificationDbViewModel>((SpecificationDbViewModel specificationDbViewModel) => ExecuteAddSpecicifationDetailEntryCommand(specificationDbViewModel));
        DeleteSpecificationDetailEntryCommand = new Command<SpecificationDetailDbViewModel>((SpecificationDetailDbViewModel specsDetailViewModel) => ExecuteDeleteSpecificationDetailEntryCommand(specsDetailViewModel));
        AddSpecicifationGroupEntriesCommand = new Command(() => ExecuteAddSpecicifationGroupEntriesCommand());
        DeleteSpecificationGroupEntryCommand = new Command<SpecificationDbViewModel>((SpecificationDbViewModel specificationDbViewModel) => ExecuteDeleteSpecicifationGroupEntriesCommand(specificationDbViewModel));

Properties

    public ICommand AddSpecicifationDetailEntryCommand { get; }
    public ICommand DeleteSpecificationDetailEntryCommand { get; }
    public ICommand AddSpecicifationGroupEntriesCommand { get; }
    public ICommand DeleteSpecificationGroupEntryCommand { get; }


来源:https://stackoverflow.com/questions/65343752/how-to-bind-index-of-item-in-nested-bindablelayout-itemssource-to-commandparamet

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!