MAUI中嵌套StackLayout绑定InvHeader与对应InvItems的问题
MAUI 主详情页面关联数据显示问题
我正在MAUI中开发一个主详情页面,类似发票列表页面:展示多个发票表头,每个表头下方显示对应的发票明细项。
现有两个关联的SQL Server表(发票表头表和发票明细表),我已经把数据分别加载到ViewModel的两个列表中:InvHeaders(发票表头集合)和InvItems(发票明细集合)。
现在的需求是:滚动显示所有发票,每个发票表头下方只显示与之CustId匹配的发票明细项。目前的问题是,每个表头下方都会显示所有发票明细,无法过滤出对应CustId的项。
我尝试用转换器InvItemsForCustIdConverter来过滤,但始终无法把当前表头的CustId作为参数传递给转换器,参数一直是null。
当前代码
XAML页面
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Name="parentView" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:converters="clr-namespace:MauiClient" x:Class="MauiClient.Invoice" Title="Invoice"> <ContentPage.Resources> <ResourceDictionary> <converters:InvItemsForCustIdConverter x:Key="InvItemsForCustIdConverter" /> </ResourceDictionary> </ContentPage.Resources> <ScrollView> <StackLayout> <!-- Header section --> <Label Text="Hospitals" FontSize="Title" /> <CollectionView ItemsSource="{Binding invHeaders}" BackgroundColor="Blue"> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout> <Label Text="{Binding Company}" /> <Label Text="{Binding Addr1}" /> <Label Text="{Binding Phone}" /> <Label Text="{Binding CustId}" BackgroundColor="Red" TextColor="White" /> <!-- Invoice items section --> <Label Text="Tech Times" FontSize="Title" BackgroundColor="Yellow" TextColor="Black"/> <StackLayout BindingContext="{Binding BindingContext, Source={x:Reference parentView}}" BackgroundColor="Green"> <StackLayout BindableLayout.ItemsSource="{Binding InvItems, Converter={StaticResource InvItemsForCustIdConverter}, ConverterParameter={Binding CustId}}"> <BindableLayout.ItemTemplate> <DataTemplate> <StackLayout Orientation="Horizontal" Spacing="20"> <Label Text="{Binding ItemId}" /> <Label Text="{Binding CustId}" BackgroundColor="Red" TextColor="White" /> </StackLayout> </DataTemplate> </BindableLayout.ItemTemplate> </StackLayout> </StackLayout> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </StackLayout> </ScrollView> </ContentPage>
ViewModel代码
public class InvoiceViewModel { public List<InvHeader> InvHeaders { get; set; } public List<InvItem> InvItems { get; set; } }
转换器代码
// 尝试过多种ConverterParameter写法,参数始终为null: // <StackLayout BindableLayout.ItemsSource="{Binding InvItems, Converter={StaticResource InvItemsForCustIdConverter}, ConverterParameter={Binding InvHeader.CustId}}"> // <StackLayout BindableLayout.ItemsSource="{Binding InvItems, Converter={StaticResource InvItemsForCustIdConverter}, ConverterParameter={Binding CustId}}"> // <StackLayout BindableLayout.ItemsSource="{Binding InvItems, Converter={StaticResource InvItemsForCustIdConverter}, ConverterParameter={Binding CustId, Source={x:Reference parentView}, Path=InvHeader}}"> public class InvItemsForCustIdConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value; // 返回全部明细时能显示,但无法过滤 if (value is List<InvItem> invItems && parameter is string custId) { return invItems.Where(x => x.CustId == custId).ToList(); } return null; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
问题核心
无法将当前CollectionView项(InvHeader)的CustId传递给转换器的ConverterParameter,导致无法按CustId过滤发票明细。
解决方案
方案1:修改数据模型,建立关联(推荐)
直接在InvHeader类中添加InvItems属性,加载数据时把对应明细关联到表头:
public class InvHeader { // 原有属性 public string Company { get; set; } public string Addr1 { get; set; } public string Phone { get; set; } public string CustId { get; set; } // 添加关联属性 public List<InvItem> InvItems { get; set; } }
加载数据时关联:
// 假设已加载invHeaders和invItems foreach(var header in invHeaders) { header.InvItems = invItems.Where(item => item.CustId == header.CustId).ToList(); }
然后修改XAML,直接绑定当前表头的InvItems,无需转换器:
<!-- 替换原有的明细部分 --> <Label Text="Tech Times" FontSize="Title" BackgroundColor="Yellow" TextColor="Black"/> <StackLayout BindableLayout.ItemsSource="{Binding InvItems}"> <BindableLayout.ItemTemplate> <DataTemplate> <StackLayout Orientation="Horizontal" Spacing="20"> <Label Text="{Binding ItemId}" /> <Label Text="{Binding CustId}" BackgroundColor="Red" TextColor="White" /> </StackLayout> </DataTemplate> </BindableLayout.ItemTemplate> </StackLayout>
方案2:用MultiBinding传递多个值(不修改模型)
如果不想修改数据模型,可以用MultiBinding把InvItems和当前CustId一起传给转换器:
- 创建多值转换器:
public class InvItemsForCustIdMultiConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if(values.Length == 2 && values[0] is List<InvItem> invItems && values[1] is string custId) { return invItems.Where(x => x.CustId == custId).ToList(); } return null; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
- 在XAML资源中添加转换器:
<converters:InvItemsForCustIdMultiConverter x:Key="InvItemsForCustIdMultiConverter" />
- 修改明细部分的绑定:
<Label Text="Tech Times" FontSize="Title" BackgroundColor="Yellow" TextColor="Black"/> <StackLayout> <StackLayout.BindableLayout.ItemsSource> <MultiBinding Converter="{StaticResource InvItemsForCustIdMultiConverter}"> <Binding Path="BindingContext.InvItems" Source="{x:Reference parentView}" /> <Binding Path="CustId" /> <!-- 这里绑定当前InvHeader的CustId --> </MultiBinding> </StackLayout.BindableLayout.ItemsSource> <BindableLayout.ItemTemplate> <DataTemplate> <StackLayout Orientation="Horizontal" Spacing="20"> <Label Text="{Binding ItemId}" /> <Label Text="{Binding CustId}" BackgroundColor="Red" TextColor="White" /> </StackLayout> </DataTemplate> </BindableLayout.ItemTemplate> </StackLayout>
内容的提问来源于stack exchange,提问作者Exquirentibus
相关产品推荐
相关产品推荐

