如何将嵌套 BindableLayout.ItemsSource 中的项目索引绑定到 CommandParameter - Xamarin
How to bind index of item in nested BindableLayout.ItemsSource to CommandParameter - Xamarin
我想知道如何从 XAML 中的列表中获取索引。
上下文
一个产品有多个规格categories/groups,其中包含规格详情。
- 产品和成分 是规格组。
- Land van afkomst : Nederland 是规格详情
在 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; }
- 不要打扰您的索引,只需将对象作为命令的参数发回即可。
Command="{...}" //same binding
CommandParameter="{Binding .}" //new line
- 并在 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 顺便说一句。
我想知道如何从 XAML 中的列表中获取索引。
上下文
一个产品有多个规格categories/groups,其中包含规格详情。
- 产品和成分 是规格组。
- Land van afkomst : Nederland 是规格详情
在 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; }
- 不要打扰您的索引,只需将对象作为命令的参数发回即可。
Command="{...}" //same binding
CommandParameter="{Binding .}" //new line
- 并在 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 顺便说一句。