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

在.NET MAUI(.NET8)中创建含多IView绑定属性的ContentView组件

.NET 8 MAUI 可复用翻转卡片组件实现方案

一、修正组件XAML结构

原XAML中Border直接嵌套多个ContentPresenter不符合布局规则,需用VerticalStackLayout统一包裹,同时为Body部分创建层叠容器实现翻转切换:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:converters="clr-namespace:YourAppNamespace.Converters"
             x:Class="LanguageInUse.Components.CardStack.FlipCard"
             x:Name="This">
    <ContentView.Resources>
        <converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
    </ContentView.Resources>
    <VerticalStackLayout>
        <Border StrokeShape="RoundRectangle 10" StrokeThickness="4" Stroke="CadetBlue">
            <VerticalStackLayout>
                <!-- 头部区域 -->
                <ContentPresenter x:Name="HeaderPresenter" />
                
                <!-- 可翻转的主体区域 -->
                <Grid x:Name="BodyContainer" Padding="0" Margin="0">
                    <ContentPresenter x:Name="FrontBodyPresenter" 
                                     IsVisible="{Binding Source={x:Reference This}, Path=IsFlipped, Converter={StaticResource InverseBooleanConverter}}" />
                    <ContentPresenter x:Name="BackBodyPresenter" 
                                     IsVisible="{Binding Source={x:Reference This}, Path=IsFlipped}" />
                </Grid>
                
                <!-- 底部区域 -->
                <ContentPresenter x:Name="FooterPresenter" />
            </VerticalStackLayout>
        </Border>
    </VerticalStackLayout>
</ContentView>

需添加反向布尔转换器(放在项目Converters文件夹):

namespace YourAppNamespace.Converters;

public class InverseBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value is bool b && !b;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value is bool b && !b;
    }
}

二、实现BindableProperty与翻转逻辑

补充Footer、FrontBody、BackBody的绑定属性,添加翻转状态控制与交互逻辑:

namespace LanguageInUse.Components.CardStack;

public partial class FlipCard : ContentView
{
    // 头部内容属性
    public static readonly BindableProperty HeaderProperty = 
        BindableProperty.Create(nameof(Header), typeof(IView), typeof(FlipCard), propertyChanged: OnHeaderChanged);

    public IView Header
    {
        get => (IView)GetValue(HeaderProperty);
        set => SetValue(HeaderProperty, value);
    }

    // 底部内容属性
    public static readonly BindableProperty FooterProperty = 
        BindableProperty.Create(nameof(Footer), typeof(IView), typeof(FlipCard), propertyChanged: OnFooterChanged);

    public IView Footer
    {
        get => (IView)GetValue(FooterProperty);
        set => SetValue(FooterProperty, value);
    }

    // 正面内容属性
    public static readonly BindableProperty FrontBodyProperty = 
        BindableProperty.Create(nameof(FrontBody), typeof(IView), typeof(FlipCard), propertyChanged: OnFrontBodyChanged);

    public IView FrontBody
    {
        get => (IView)GetValue(FrontBodyProperty);
        set => SetValue(FrontBodyProperty, value);
    }

    // 背面内容属性
    public static readonly BindableProperty BackBodyProperty = 
        BindableProperty.Create(nameof(BackBody), typeof(IView), typeof(FlipCard), propertyChanged: OnBackBodyChanged);

    public IView BackBody
    {
        get => (IView)GetValue(BackBodyProperty);
        set => SetValue(BackBodyProperty, value);
    }

    // 翻转状态属性
    public static readonly BindableProperty IsFlippedProperty = 
        BindableProperty.Create(nameof(IsFlipped), typeof(bool), typeof(FlipCard), defaultValue: false);

    public bool IsFlipped
    {
        get => (bool)GetValue(IsFlippedProperty);
        set => SetValue(IsFlippedProperty, value);
    }

    public FlipCard()
    {
        InitializeComponent();
        // 绑定点击翻转手势
        BodyContainer.GestureRecognizers.Add(new TapGestureRecognizer
        {
            Command = new Command(ToggleFlip)
        });
    }

    private void ToggleFlip()
    {
        // 添加3D翻转动画
        if (IsFlipped)
        {
            FrontBodyPresenter.RotateYTo(0, 300);
            BackBodyPresenter.RotateYTo(90, 300);
        }
        else
        {
            FrontBodyPresenter.RotateYTo(90, 300);
            BackBodyPresenter.RotateYTo(0, 300);
        }
        // 延迟更新状态配合动画节奏
        _ = Task.Delay(150).ContinueWith(_ => IsFlipped = !IsFlipped);
    }

    // 属性变更回调
    private static void OnHeaderChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var card = (FlipCard)bindable;
        card.HeaderPresenter.Content = newValue as View;
    }

    private static void OnFooterChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var card = (FlipCard)bindable;
        card.FooterPresenter.Content = newValue as View;
    }

    private static void OnFrontBodyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var card = (FlipCard)bindable;
        card.FrontBodyPresenter.Content = newValue as View;
    }

    private static void OnBackBodyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var card = (FlipCard)bindable;
        card.BackBodyPresenter.Content = newValue as View;
    }
}

三、批量生成卡片的使用示例

在页面中通过CollectionView绑定集合批量渲染卡片:

<CollectionView ItemsSource="{Binding CardItems}" Margin="10">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <components:FlipCard HeightRequest="280" Margin="0,0,0,15">
                <!-- 头部内容 -->
                <components:FlipCard.Header>
                    <Label Text="{Binding CardTitle}" FontSize="Large" FontWeight="Bold" Padding="12"/>
                </components:FlipCard.Header>
                
                <!-- 正面内容 -->
                <components:FlipCard.FrontBody>
                    <VerticalStackLayout Padding="12">
                        <Label Text="正面展示" FontSize="Medium"/>
                        <Label Text="{Binding FrontContent}" Margin="0,10,0,0" LineBreakMode="WordWrap"/>
                    </VerticalStackLayout>
                </components:FlipCard.FrontBody>
                
                <!-- 背面内容 -->
                <components:FlipCard.BackBody>
                    <VerticalStackLayout Padding="12">
                        <Label Text="背面详情" FontSize="Medium" TextColor="#B3261E"/>
                        <Label Text="{Binding BackContent}" Margin="0,10,0,0" LineBreakMode="WordWrap"/>
                    </VerticalStackLayout>
                </components:FlipCard.BackBody>
                
                <!-- 底部内容 -->
                <components:FlipCard.Footer>
                    <Label Text="{Binding FooterInfo}" FontSize="Small" Padding="12" HorizontalTextAlignment="End"/>
                </components:FlipCard.Footer>
            </components:FlipCard>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

关键修复点说明

  • 修正了原XAML中Border直接嵌套多个ContentPresenter的布局错误,改用VerticalStackLayout统一包裹
  • 为Body部分使用Grid实现前后内容层叠,通过IsFlipped属性控制显示切换
  • 补充了Footer、FrontBody、BackBody的BindableProperty实现,统一属性变更回调逻辑
  • 添加了点击翻转手势与3D动画,提升交互体验
  • 修复了原代码中HeaderContent的命名错误,匹配XAML中的ContentPresenter实例名

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 05:04:59