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

MAUI CollectionView:父项触发子项按钮隐藏的实现方案求助

解决方案:基于MVVM的优雅实现(适配多父项场景)

1. 重构数据模型,引入ObservableObject

让父项和子项模型继承ObservableObject(MAUI推荐使用CommunityToolkit.Mvvm库),实现属性变更自动通知UI,完美适配多父项场景:

using CommunityToolkit.Mvvm.ComponentModel;

// 子项模型
public partial class ChildPart : ObservableObject
{
    [ObservableProperty]
    private string _childOwned;

    [ObservableProperty]
    private bool _isOwned;

    // 用于Preferences存储的唯一标识
    public string Id { get; set; }
}

// 父项模型
public partial class ParentPart : ObservableObject
{
    [ObservableProperty]
    private string _parentOwned;

    [ObservableProperty]
    private bool _isAllOwned;

    public List<ChildPart> Child { get; set; } = new();

    // 用于Preferences存储的唯一标识
    public string Id { get; set; }

    // 从Preferences加载已保存的状态
    public void LoadState()
    {
        IsAllOwned = Preferences.Get($"Parent_{Id}_AllOwned", false);
        ParentOwned = IsAllOwned ? "已全拥有" : "标记全拥有";

        foreach (var child in Child)
        {
            child.IsOwned = Preferences.Get($"Child_{child.Id}_Owned", false);
            child.ChildOwned = child.IsOwned ? "已拥有" : "标记拥有";
        }
    }
}

2. 修改XAML,绑定子按钮可见性到父项状态

利用RelativeSource AncestorType实现子项绑定到父项的IsAllOwned属性,直接控制子按钮的显示/隐藏,无需手动操作控件:

<ContentPage.Resources>
    <local:BoolToColorConverter x:Key="BoolToColorConverter"/>
    <local:InverseBoolConverter x:Key="InverseBoolConverter"/>
</ContentPage.Resources>

<CollectionView x:Name="ParentCollection" ItemsSource="{Binding Parent}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Frame>
                <Button Text="{Binding ParentOwned}" 
                        Clicked="ParentOwnedButtonClick" 
                        WidthRequest="100"
                        BackgroundColor="{Binding IsAllOwned, Converter={StaticResource BoolToColorConverter}}"/>
                <ScrollView>
                    <CollectionView x:Name="ChildCollection" ItemsSource="{Binding Child}">
                        <CollectionView.ItemTemplate>
                            <DataTemplate>
                                <Frame>
                                    <!-- 父项标记全拥有时,隐藏子按钮 -->
                                    <Button Text="{Binding ChildOwned}" 
                                            Clicked="ChildOwnedButtonClick" 
                                            WidthRequest="100"
                                            IsVisible="{Binding Source={RelativeSource AncestorType={x:Type local:ParentPart}}, Path=IsAllOwned, Converter={StaticResource InverseBoolConverter}}"/>
                                </Frame>
                            </DataTemplate>
                        </CollectionView.ItemTemplate>
                    </CollectionView>
                </ScrollView>
            </Frame>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

3. 实现辅助转换器

需要两个简单的转换器,用于布尔值与UI状态的转换:

using System.Globalization;

// 布尔值转按钮背景色
public class BoolToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? Colors.Green : Colors.Gray;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

// 布尔值取反,控制子按钮可见性
public class InverseBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return !(bool)value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

4. 处理按钮点击事件,同步状态到Preferences

private void ParentOwnedButtonClick(object sender, EventArgs e)
{
    var parent = (sender as Button).BindingContext as ParentPart;

    parent.IsAllOwned = !parent.IsAllOwned;
    parent.ParentOwned = parent.IsAllOwned ? "已全拥有" : "标记全拥有";

    // 保存父项状态到Preferences
    Preferences.Set($"Parent_{parent.Id}_AllOwned", parent.IsAllOwned);

    // 可选:自动同步所有子项的拥有状态
    if (parent.IsAllOwned)
    {
        foreach (var child in parent.Child)
        {
            child.IsOwned = true;
            child.ChildOwned = "已拥有";
            Preferences.Set($"Child_{child.Id}_Owned", true);
        }
    }
}

private void ChildOwnedButtonClick(object sender, EventArgs e)
{
    var child = (sender as Button).BindingContext as ChildPart;

    child.IsOwned = !child.IsOwned;
    child.ChildOwned = child.IsOwned ? "已拥有" : "标记拥有";

    Preferences.Set($"Child_{child.Id}_Owned", child.IsOwned);
}

5. 页面初始化加载状态

在页面的OnAppearing方法中,加载所有父项和子项的已保存状态:

protected override void OnAppearing()
{
    base.OnAppearing();
    if (ParentCollection.ItemsSource is List<ParentPart> parentList)
    {
        foreach (var parent in parentList)
        {
            parent.LoadState();
        }
    }
}

为什么不推荐遍历子控件?

MAUI的CollectionView是虚拟化控件,未显示的子项不会被渲染,遍历控件的方式容易出现状态遗漏,且代码耦合度高,后续维护难度大。而上述MVVM数据驱动的方式,完全基于属性绑定适配多父项场景,代码简洁且易维护。

内容的提问来源于stack exchange,提问作者Joe Schmidt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 03:01:09