如何将嵌套 BindableLayout.ItemsSource 中的项目索引绑定到 CommandParameter - Xamarin

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

我想知道如何从 XAML 中的列表中获取索引。

上下文

一个产品有多个规格categories/groups,其中包含规格详情。

在 XAML 代码中,我使用了嵌套列表。应用程序需要传递索引以便用户可以正确地删除和添加规范。

需要传递 Binding Source="0" /> 和 CommandParameter="0" 处的 index 而不是 "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}" />

规格组型号:

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));
    }
}

规格详细型号:

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));
    }
}

我正在使用 MultiBinder 转换器将多个值传递给命令。 ViewModel 中删除规范的 1 个方法:

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

因为在 XAML 中获取索引似乎非常困难,所以我创建了一个不同的解决方案。我仍然希望有人知道如何在嵌套的堆栈布局列表中获取索引。我的老师说,在性能不需要时循环处理是不好的做法。

    <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}" />

添加和删除规范的方法

 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;
            }
        }
    }

在视图模型的构造函数中创建命令

        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));

属性

    public ICommand AddSpecicifationDetailEntryCommand { get; }
    public ICommand DeleteSpecificationDetailEntryCommand { get; }
    public ICommand AddSpecicifationGroupEntriesCommand { get; }
    public ICommand DeleteSpecificationGroupEntryCommand { get; }
  1. 不要打扰您的索引,只需将对象作为命令的参数发回即可。
Command="{...}"  //same binding
CommandParameter="{Binding .}"  //new line
  1. 并在 ViewModel 中使用正确的参数定义命令。
    public ICommand<SpecificationDetailDbViewModel> DeleteSpecificationEntryCommand => new Command<SpecificationDetailDbViewModel>(ExecuteDeleteSpecificationEntryCommand);

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

你也可以使用 groups in the list view 顺便说一句。