嵌套ObservableCollections中Label的Command属性绑定问题
解决嵌套CollectionView中命令绑定到上级数据上下文的问题
我有两层嵌套的ObservableCollection,第一层是星期日期列表,第二层是对应日期的可用时段列表。现在要实现点击REMOVE标签删除指定时段,但无法正确触发RemoveHour命令,代码如下:
后台代码
public static ObservableCollection<Calendar> CalendarDays { get; set; } = new ObservableCollection<Calendar>(); public AgendaAutoDispon() { InitializeComponent(); InitializeCalendar(); CalendarDays.Add(new Calendar(DateTime.Today)); CalendarDays.Add(new Calendar(DateTime.Today.AddDays(1))); CalendarDays.Add(new Calendar(DateTime.Today.AddDays(2))); CollectionViewDatas.BindingContext = CalendarDays; } public class Calendar : INotifyPropertyChanged { public Picker HourPicker { get; set; } = new Picker(); public ObservableCollection<string> Hours { get; set; } = new(); public ObservableCollection<string> HoursReceipt { get; set; } = new(); public Calendar(DateTime day) { Day = day; for (int i = 6; i <= 22; i++) { HoursReceipt.Add((i < 10 ? "0" + i.ToString() : i.ToString()) + ":00"); HoursReceipt.Add((i < 10 ? "0" + i.ToString() : i.ToString()) + ":30"); } NotifyPropertyChanged("HoursReceipt"); } private string _DateShow; public string DateShow { get => _DateShow; set { _DateShow = value; NotifyPropertyChanged("DateShow"); } } private DateTime _Day { get; set; } public DateTime Day { get => _Day; set { _Day = value; switch (value.DayOfWeek) { case DayOfWeek.Sunday: WeekDay = "Sunday"; break; case DayOfWeek.Monday: WeekDay = "Monday"; break; case DayOfWeek.Tuesday: WeekDay = "Tuesday"; break; case DayOfWeek.Wednesday: WeekDay = "Wednesday"; break; case DayOfWeek.Thursday: WeekDay = "Thursday"; break; case DayOfWeek.Friday: WeekDay = "Friday"; break; case DayOfWeek.Saturday: WeekDay = "Saturday"; break; } DateShow = value.ToString("dd/MM/yyyy"); NotifyPropertyChanged("Day"); } } private string _WeekDay; public string WeekDay { get => _WeekDay; set { _WeekDay = value; NotifyPropertyChanged("WeekDay"); } } public string HourChangedEvent { set { if (!Hours.Contains(value)) { Hours.Add(value); Hours = new ObservableCollection<string>(Hours.OrderBy(i => i)); NotifyPropertyChanged("Hours"); } } } public Command RemoveHour { get { return new Command((arg) => { Console.WriteLine("I SHOULD REMOVE =====> "+(string)arg); }); } } public event PropertyChangedEventHandler PropertyChanged; void NotifyPropertyChanged(string propChanged) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propChanged)); } }
XAML代码
<CollectionView x:Name="CollectionViewDates" BindingContext="{Binding CalendarDays}" ItemsSource="{Binding .}" VerticalOptions="Center" Margin="0,10,0,0"> <CollectionView.ItemsLayout> <GridItemsLayout Orientation="Horizontal" HorizontalItemSpacing="10" /> </CollectionView.ItemsLayout> <!-- WEEK DAYS --> <CollectionView.ItemTemplate> <DataTemplate> <Frame CornerRadius="15" Padding="0" Margin="5" HasShadow="False" BorderColor="LightGray"> <Grid HeightRequest="300" WidthRequest="200" HorizontalOptions="CenterAndExpand"> <Grid.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="30" /> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Label Text="{Binding WeekDay}" Grid.Row="0" TextColor="#4D4D4D" FontSize="20" HorizontalTextAlignment="Center" FontAttributes="Bold" Margin="0,5,0,0" /> <Label Text="{Binding DateShow}" Grid.Row="1" Margin="0,3,0,0" TextColor="#4D4D4D" FontSize="18" HorizontalTextAlignment="Center" /> <!-- HOURS --> <CollectionView Grid.Row="3" ItemsSource="{Binding Hours}"> <CollectionView.ItemsLayout> <GridItemsLayout Orientation="Vertical" VerticalItemSpacing="5" /> </CollectionView.ItemsLayout> <CollectionView.ItemTemplate> <DataTemplate> <Grid HeightRequest="30"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="30" /> </Grid.ColumnDefinitions> <Label Grid.Column="0" Text="{Binding .}" HorizontalTextAlignment="Center" /> <Label Grid.Column="1" Text="REMOVE" FontSize="20" TextColor="Red" Padding="0" HorizontalTextAlignment="Center"> <Label.GestureRecognizers> <TapGestureRecognizer Command="{Binding WHAT SHOULD I HAVE HERE??? }" CommandParameter="{Binding .}" /> </Label.GestureRecognizers> </Label> </Grid> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </Grid> </Frame> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView>
解决方案
要在内层CollectionView中绑定到上级Calendar对象的RemoveHour命令,需要通过RelativeSource定位到外层的CollectionView,从而获取正确的BindingContext。修改XAML中的TapGestureRecognizer绑定:
<TapGestureRecognizer Command="{Binding BindingContext.RemoveHour, Source={RelativeSource AncestorType={x:Type CollectionView}, AncestorLevel=2}}" CommandParameter="{Binding .}" />
绑定逻辑说明
AncestorType={x:Type CollectionView}:指定向上查找的元素类型为CollectionViewAncestorLevel=2:表示跳过当前内层的Hours列表CollectionView,找到第2层的CalendarDays列表CollectionView,它的BindingContext正是包含RemoveHour命令的Calendar实例BindingContext.RemoveHour:获取该CollectionView上下文对象中的RemoveHour命令
完善删除逻辑
当前RemoveHour仅打印日志,需补充实际删除逻辑,且ObservableCollection本身支持自动通知UI更新,无需重新实例化集合:
public Command RemoveHour { get { return new Command((arg) => { var hourToRemove = arg as string; if (!string.IsNullOrEmpty(hourToRemove) && Hours.Contains(hourToRemove)) { Hours.Remove(hourToRemove); // 无需重新创建ObservableCollection,Remove操作会自动触发UI更新 } }); } }
内容的提问来源于stack exchange,提问作者Éder Rocha
相关产品推荐
相关产品推荐

