You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套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}:指定向上查找的元素类型为CollectionView
  • AncestorLevel=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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 16:55:30