增加了动画以及用Style重写了样式。

FlipPanel.cs控制逻辑

using Avalonia;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using System;

namespace AvaloniaUI
{
    [TemplatePart("PART_FrontHost", typeof(Border))]
    [TemplatePart("PART_BackHost", typeof(Border))]
    [TemplatePart("PART_FlipButton", typeof(ToggleButton))]
    public class FlipPanel2 : TemplatedControl
    {
        // ========== 依赖属性 ==========
        public static readonly StyledProperty<Control?> FrontContentProperty =
            AvaloniaProperty.Register<FlipPanel2, Control?>(nameof(FrontContent));

        public static readonly StyledProperty<Control?> BackContentProperty =
            AvaloniaProperty.Register<FlipPanel2, Control?>(nameof(BackContent));

        public static readonly StyledProperty<bool> IsFlippedProperty =
            AvaloniaProperty.Register<FlipPanel2, bool>(nameof(IsFlipped));

        public static readonly StyledProperty<double> FlipAngleProperty =
            AvaloniaProperty.Register<FlipPanel2, double>(nameof(FlipAngle), 0d);

        // ========== CLR 包装 ==========
        public Control? FrontContent
        {
            get => GetValue(FrontContentProperty);
            set => SetValue(FrontContentProperty, value);
        }

        public Control? BackContent
        {
            get => GetValue(BackContentProperty);
            set => SetValue(BackContentProperty, value);
        }

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

        public double FlipAngle
        {
            get => GetValue(FlipAngleProperty);
            set => SetValue(FlipAngleProperty, value);
        }

        // ========== 模板部件 ==========
        private ToggleButton? flipButton;

        public FlipPanel2()
        {
            // 监听属性变化
            this.GetObservable(IsFlippedProperty).Subscribe(_ =>
            {
                UpdatePseudoClasses();
            });
        }

        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
        {
            base.OnApplyTemplate(e);
            flipButton = e.NameScope.Find<ToggleButton>("PART_FlipButton");

            if (flipButton != null)
                flipButton.Click += (_, _) => ToggleFlipped();

            UpdatePseudoClasses();
        }

        private void ToggleFlipped()
        {
            IsFlipped = !IsFlipped;
        }

        private void UpdatePseudoClasses()
        {
            PseudoClasses.Set(":flipped", IsFlipped);
        }
    }
}

FlipPanelAlternateTemplate.axaml代码

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:AvaloniaUI"
        x:Class="AvaloniaUI.FlipPanelAlternateTemplate"
        Width="350" Height="300">

    <Window.Resources>
        <ControlTemplate x:Key="FancyFlipPanel" TargetType="local:FlipPanel2">
            <Grid>
                <!-- FRONT -->
                <Border x:Name="PART_FrontHost"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="{TemplateBinding CornerRadius}"
                        Background="#E3F2FD"
                        RenderTransformOrigin="0.5,0.5">
                    <ContentPresenter Content="{TemplateBinding FrontContent}"
                                      HorizontalAlignment="Center"
                                      VerticalAlignment="Center"/>
                    <Border.Styles>

                        <!-- 翻转到背面 -->
                        <Style Selector="local|FlipPanel2:flipped /template/ Border#PART_FrontHost">
                            <Style.Animations>
                                <Animation Duration="0:0:0.4" Easing="SineEaseInOut" FillMode="Forward">
                                    <KeyFrame Cue="0%">
                                        <Setter Property="ScaleTransform.ScaleY" Value="1"/>
                                        <Setter Property="BlurEffect.Radius" Value="0"/>
                                    </KeyFrame>
                                    <KeyFrame Cue="100%">
                                        <Setter Property="ScaleTransform.ScaleY" Value="0"/>
                                        <Setter Property="BlurEffect.Radius" Value="30"/>
                                    </KeyFrame>
                                </Animation>
                            </Style.Animations>
                        </Style>

                        <!-- 回到正面 -->
                        <Style Selector="local|FlipPanel2:not(:flipped) /template/ Border#PART_FrontHost">
                            <Style.Animations>
                                <Animation Duration="0:0:0.4" Easing="SineEaseInOut" FillMode="Forward">
                                    <KeyFrame Cue="0%">
                                        <Setter Property="ScaleTransform.ScaleY" Value="0"/>
                                        <Setter Property="BlurEffect.Radius" Value="30"/>
                                    </KeyFrame>
                                    <KeyFrame Cue="100%">
                                        <Setter Property="ScaleTransform.ScaleY" Value="1"/>
                                        <Setter Property="BlurEffect.Radius" Value="0"/>
                                    </KeyFrame>
                                </Animation>
                            </Style.Animations>
                        </Style>
                    </Border.Styles>
                </Border>

                <!-- BACK -->
                <Border x:Name="PART_BackHost"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="{TemplateBinding CornerRadius}"
                        Background="#FFF3E0"
                        RenderTransformOrigin="0.5,0.5">
                    <ContentPresenter Content="{TemplateBinding BackContent}"
                                      HorizontalAlignment="Center"
                                      VerticalAlignment="Center"/>

                    <Border.Styles>
                        <Style Selector="local|FlipPanel2:flipped /template/ Border#PART_BackHost">
                            <Style.Animations>
                                <Animation Duration="0:0:0.4" Easing="SineEaseInOut" FillMode="Forward">
                                    <KeyFrame Cue="0%">
                                        <Setter Property="ScaleTransform.ScaleY" Value="0"/>
                                        <Setter Property="BlurEffect.Radius" Value="30"/>
                                    </KeyFrame>
                                    <KeyFrame Cue="100%">
                                        <Setter Property="ScaleTransform.ScaleY" Value="1"/>
                                        <Setter Property="BlurEffect.Radius" Value="0"/>
                                    </KeyFrame>
                                </Animation>
                            </Style.Animations>
                        </Style>

                        <Style Selector="local|FlipPanel2:not(:flipped) /template/ Border#PART_BackHost">
                            <Style.Animations>
                                <Animation Duration="0:0:0.4" Easing="SineEaseInOut" FillMode="Forward">
                                    <KeyFrame Cue="0%">
                                        <Setter Property="ScaleTransform.ScaleY" Value="1"/>
                                        <Setter Property="BlurEffect.Radius" Value="0"/>
                                    </KeyFrame>
                                    <KeyFrame Cue="100%">
                                        <Setter Property="ScaleTransform.ScaleY" Value="0"/>
                                        <Setter Property="BlurEffect.Radius" Value="30"/>
                                    </KeyFrame>
                                </Animation>
                            </Style.Animations>
                        </Style>
                    </Border.Styles>
                </Border>

                <!-- 按钮 -->
                <ToggleButton x:Name="PART_FlipButton"
                              HorizontalAlignment="Right"
                              VerticalAlignment="Bottom"
                              Margin="5"
                              Padding="10,0"
                              FontSize="14"
                              Content="˄">
                    <ToggleButton.Styles>
                        <Style Selector="local|FlipPanel2:flipped /template/ ToggleButton#PART_FlipButton">
                            <Setter Property="Content" Value="˅"/>
                            <Setter Property="VerticalAlignment" Value="Top"/>
                            <Style.Animations>
                                <Animation Duration="0:0:0.4" Easing="SineEaseInOut" FillMode="Forward">
                                    <KeyFrame Cue="0%">
                                        <Setter Property="TranslateTransform.Y" Value="150"/>
                                    </KeyFrame>
                                    <KeyFrame Cue="100%">
                                        <Setter Property="TranslateTransform.Y" Value="0"/>
                                    </KeyFrame>
                                </Animation>
                            </Style.Animations>
                        </Style>
                        <Style Selector="local|FlipPanel2:not(:flipped) ToggleButton#PART_FlipButton">
                            <Setter Property="Content" Value="˄"/>
                            <Setter Property="VerticalAlignment" Value="Bottom"/>
                            <Style.Animations>
                                <Animation Duration="0:0:0.4" Easing="SineEaseInOut" FillMode="Forward">
                                    <KeyFrame Cue="0%">
                                        <Setter Property="TranslateTransform.Y" Value="-150"/>
                                    </KeyFrame>
                                    <KeyFrame Cue="100%">
                                        <Setter Property="TranslateTransform.Y" Value="0"/>
                                    </KeyFrame>
                                </Animation>
                            </Style.Animations>
                        </Style>
                    </ToggleButton.Styles>
                </ToggleButton>
            </Grid>
        </ControlTemplate>
    </Window.Resources>

    <Grid>
        <local:FlipPanel2 Template="{StaticResource FancyFlipPanel}"
                         Width="300" Height="200"
                         BorderBrush="DarkOrange"
                         BorderThickness="3"
                         CornerRadius="6"
                         Margin="20">
            <local:FlipPanel2.FrontContent>
                <TextBlock Text="Front Side"
                           FontSize="18"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"/>
            </local:FlipPanel2.FrontContent>
            <local:FlipPanel2.BackContent>
                <TextBlock Text="Back Side"
                           FontSize="18"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"/>
            </local:FlipPanel2.BackContent>
        </local:FlipPanel2>
    </Grid>
</Window>

FlipPanelAlternateTemplate.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Shares.Avalonia;

namespace AvaloniaUI;

public partial class FlipPanelAlternateTemplate : Window
{
    public FlipPanelAlternateTemplate()
    {
        InitializeComponent();
    }
}

运行效果

image

 

posted on 2025-11-25 13:29  dalgleish  阅读(0)  评论(0)    收藏  举报