MAUI中无法在后台代码访问嵌套CollectionView组件问题求助
开发MAUI的FlyoutMenuPage时,父CollectionView(collectionViewFlyout)的ItemTemplate内嵌套了子CollectionView(subMenuItemsCollectionView),后台代码无法识别这个子组件,报错无法访问subMenuItemsCollectionView。
XAML代码
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="Internal_SME_UI.Views.FlyoutMenuPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:helpers="clr-namespace:Internal_SME_UI.Helpers" xmlns:models="clr-namespace:SME_Data.Models;assembly=SME_Data" xmlns:vm="clr-namespace:Internal_SME_UI.ViewModels" Title="FlyoutMenuPage" x:DataType="vm:FlyoutMenuViewModel"> <StackLayout Margin="10,20" HorizontalOptions="FillAndExpand" Spacing="10" VerticalOptions="FillAndExpand"> <CollectionView x:Name="collectionViewFlyout" Margin="3,15,5,15" x:FieldModifier="public" ItemsSource="{Binding FlyoutItems}" SelectionChanged="collectionViewFlyout_SelectionChanged" SelectionMode="Single"> <CollectionView.ItemTemplate> <DataTemplate x:Name="subMenuItems" x:DataType="models:FlyoutItemModel"> <Grid RowDefinitions="Auto,auto"> <Grid Grid.Row="0" Padding="5,10"> <Grid.ColumnDefinitions> <ColumnDefinition Width="30" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="30" /> </Grid.ColumnDefinitions> <Label x:Name="iconLbl" FontFamily="Icon" FontSize="16" HorizontalOptions="Center" Text="{Binding MenuIcon}" TextColor="#00416d" /> <Label x:Name="titleLbl" Grid.Column="1" Margin="10,0" FontAttributes="Bold" FontSize="16" Text="{Binding Title}" TextColor="#00416d" /> </Grid> <CollectionView x:Name="subMenuItemsCollectionView" Grid.Row="1" x:FieldModifier="public" ItemsSource="{Binding SubItems}" SelectionChanged="subMenuItemsCollectionView_SelectionChanged" SelectionMode="Single"> <CollectionView.ItemTemplate> <DataTemplate x:Name="subMenuItems" x:DataType="models:FlyoutItemModel"> <Grid Margin="10,0,0,0" Padding="5,10"> <Grid.ColumnDefinitions> <ColumnDefinition Width="30" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Label x:Name="iconLblSubMenu" FontFamily="Icon" FontSize="16" HorizontalOptions="Center" Text="{Binding MenuIcon}" TextColor="#00416d" /> <Label x:Name="titleLblSubMenu" Grid.Column="1" Margin="10,0" FontAttributes="Bold" FontSize="16" Text="{Binding Title}" TextColor="#00416d" /> <VisualStateManager.VisualStateGroups> <VisualStateGroup Name="CommonStates"> <VisualState Name="Normal" /> <VisualState Name="Selected"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="#00416d" /> <Setter TargetName="titleLblSubMenu" Property="Label.TextColor" Value="White" /> <Setter TargetName="iconLblSubMenu" Property="Label.TextColor" Value="White" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Grid> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> <VisualStateManager.VisualStateGroups> <VisualStateGroup Name="CommonStates"> <VisualState Name="Normal" /> <VisualState Name="Selected"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="#00416d" /> <Setter TargetName="titleLbl" Property="Label.TextColor" Value="White" /> <Setter TargetName="iconLbl" Property="Label.TextColor" Value="White" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Grid> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </StackLayout> </ContentPage>
后台代码
using Internal_SME_UI.ViewModels; using SME_Data.Models; namespace Internal_SME_UI.Views; public partial class FlyoutMenuPage : ContentPage { private readonly FlyoutMenuViewModel _flyoutMenuViewModel; public FlyoutMenuPage(FlyoutMenuViewModel flyoutMenuViewModel) { InitializeComponent(); _flyoutMenuViewModel = flyoutMenuViewModel; BindingContext = flyoutMenuViewModel; collectionViewFlyout.SelectedItem = flyoutMenuViewModel.FlyoutItems[1]; } public FlyoutMenuPage():this(new FlyoutMenuViewModel()) { } private void collectionViewFlyout_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.CurrentSelection.FirstOrDefault() is FlyoutItemModel selectedItem) { if (selectedItem == _flyoutMenuViewModel.FlyoutItems[5]) { collectionViewFlyout.SelectedItem = e.PreviousSelection; } } } private void subMenuItemsCollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e) { collectionViewFlyout.SelectedItem = null; subMenuItemsCollectionView.SelectedItem = e.CurrentSelection; } }
问题原因
CollectionView的ItemTemplate是数据模板,模板内的控件属于每个列表项的独立实例,并非页面级全局控件。父CollectionView有多少个列表项,就会生成多少个子CollectionView实例,编译器无法确定你要访问哪一个,因此无法直接通过x:Name在后台代码中引用。
解决办法
方法1:通过事件sender获取当前子CollectionView
直接利用事件的sender参数,获取触发事件的那个子CollectionView实例,修改后的事件代码如下:
private void subMenuItemsCollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e) { collectionViewFlyout.SelectedItem = null; if (sender is CollectionView currentSubCollection) { currentSubCollection.SelectedItem = e.CurrentSelection.FirstOrDefault(); } }
方法2:MVVM绑定(推荐)
遵循MAUI的MVVM设计思想,将子CollectionView的SelectedItem绑定到数据模型,避免直接操作UI控件:
- 在
FlyoutItemModel中添加SelectedSubItem属性并实现INotifyPropertyChanged:
public class FlyoutItemModel : INotifyPropertyChanged { // 原有属性(MenuIcon、Title、SubItems等)... private FlyoutItemModel _selectedSubItem; public FlyoutItemModel SelectedSubItem { get => _selectedSubItem; set { _selectedSubItem = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
- 修改子CollectionView的XAML,绑定SelectedItem:
<CollectionView x:Name="subMenuItemsCollectionView" Grid.Row="1" ItemsSource="{Binding SubItems}" SelectedItem="{Binding SelectedSubItem, Mode=TwoWay}" SelectionChanged="subMenuItemsCollectionView_SelectionChanged" SelectionMode="Single">
- 简化后台事件逻辑(或直接移除,将选择逻辑移至ViewModel):
private void subMenuItemsCollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e) { collectionViewFlyout.SelectedItem = null; }
内容的提问来源于stack exchange,提问作者Sparsh
相关产品推荐
相关产品推荐

