499 lines
18 KiB
C#
499 lines
18 KiB
C#
using System.Windows;
|
||||
|
|
using System.Windows.Controls;
|
|||
|
|
using System.Windows.Data;
|
|||
|
|
using System.Windows.Documents;
|
|||
|
|
using System.Windows.Input;
|
|||
|
|
using System.Windows.Media;
|
|||
|
|
using System.Windows.Shapes;
|
|||
|
|
using PCL.Core.UI.Controls;
|
|||
|
|
|
|||
|
|
namespace PCL;
|
|||
|
|
|
|||
|
|
public class MyCard : AnimatedBackgroundGrid
|
|||
|
|
{
|
|||
|
|
// 动画
|
|||
|
|
private const double dropShadowIdleOpacity = 0.07d;
|
|||
|
|
private const double dropShadowHoverOpacity = 0.4d;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty TitleProperty =
|
|||
|
|
DependencyProperty.Register("Title", typeof(string), typeof(MyCard), new PropertyMetadata(""));
|
|||
|
|
|
|||
|
|
private readonly BlurBorder mainBorder;
|
|||
|
|
|
|||
|
|
// 控件
|
|||
|
|
private readonly Grid mainGrid;
|
|||
|
|
private TextBlock _MainTextBlock;
|
|||
|
|
private bool isLoad;
|
|||
|
|
|
|||
|
|
// UI 建立
|
|||
|
|
public MyCard() : base(BlurBorder.BackgroundProperty)
|
|||
|
|
{
|
|||
|
|
MainChrome = new MyDropShadow
|
|||
|
|
{
|
|||
|
|
Margin = new Thickness(-3, -3, -3, -3 - ModBase.GetWPFSize(1d)), ShadowRadius = 3d,
|
|||
|
|
Opacity = dropShadowIdleOpacity, CornerRadius = new CornerRadius(5d)
|
|||
|
|
};
|
|||
|
|
MainChrome.SetResourceReference(MyDropShadow.ColorProperty, "ColorObject1");
|
|||
|
|
Children.Insert(0, MainChrome);
|
|||
|
|
mainBorder = new BlurBorder { CornerRadius = new CornerRadius(5d), IsHitTestVisible = false };
|
|||
|
|
Children.Insert(1, mainBorder);
|
|||
|
|
mainGrid = new Grid();
|
|||
|
|
Children.Add(mainGrid);
|
|||
|
|
// 设置背景色
|
|||
|
|
SetResourceReference(BackgroundBrushProperty, "ColorBrushTransparentBackground");
|
|||
|
|
Loaded += (_, _) => Init();
|
|||
|
|
MouseEnter += MyCard_MouseEnter;
|
|||
|
|
MouseLeave += MyCard_MouseLeave;
|
|||
|
|
SizeChanged += MySizeChanged;
|
|||
|
|
MouseLeftButtonDown += MyCard_MouseLeftButtonDown;
|
|||
|
|
MouseLeftButtonUp += MyCard_MouseLeftButtonUp;
|
|||
|
|
MouseLeave += MyCard_MouseLeave_Swap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public MyDropShadow MainChrome { get; }
|
|||
|
|
|
|||
|
|
public UIElement BorderChild
|
|||
|
|
{
|
|||
|
|
get => mainBorder.Child;
|
|||
|
|
set => mainBorder.Child = value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public TextBlock MainTextBlock
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
Init(); // 当父级触发 Loaded 时,本卡片可能尚未触发 Loaded(该事件从父级向子级调用),因此这会是 null。手动触发以确保控件已加载。
|
|||
|
|
return _MainTextBlock;
|
|||
|
|
}
|
|||
|
|
set => _MainTextBlock = value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public Path MainSwap
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
Init();
|
|||
|
|
return field;
|
|||
|
|
}
|
|||
|
|
set => field = value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 属性
|
|||
|
|
public InlineCollection Inlines => MainTextBlock.Inlines;
|
|||
|
|
|
|||
|
|
public CornerRadius CornerRadius
|
|||
|
|
{
|
|||
|
|
get => MainChrome.CornerRadius;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
MainChrome.CornerRadius = value;
|
|||
|
|
mainBorder.CornerRadius = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string Title
|
|||
|
|
{
|
|||
|
|
get => (string)GetValue(TitleProperty);
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
SetValue(TitleProperty, value);
|
|||
|
|
if (_MainTextBlock is not null)
|
|||
|
|
MainTextBlock.Text = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override SolidColorBrush AnimatableBrush
|
|||
|
|
{
|
|||
|
|
get => (SolidColorBrush)mainBorder.Background;
|
|||
|
|
set => mainBorder.Background = value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override FrameworkElement AnimatableElement => mainBorder;
|
|||
|
|
public bool HasMouseAnimation { get; set; } = true;
|
|||
|
|
|
|||
|
|
private void Init()
|
|||
|
|
{
|
|||
|
|
if (isLoad)
|
|||
|
|
return;
|
|||
|
|
isLoad = true;
|
|||
|
|
// AddHandler ThemeChanged, AddressOf _BackgroundBrushChanged '已在依赖属性中实现
|
|||
|
|
// 初次加载限定
|
|||
|
|
if (MainTextBlock is null)
|
|||
|
|
{
|
|||
|
|
MainTextBlock = new TextBlock
|
|||
|
|
{
|
|||
|
|
HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top,
|
|||
|
|
Margin = new Thickness(15d, 12d, 0d, 0d), FontWeight = FontWeights.Bold, FontSize = 13d,
|
|||
|
|
IsHitTestVisible = false
|
|||
|
|
};
|
|||
|
|
MainTextBlock.SetResourceReference(TextBlock.ForegroundProperty, "ColorBrush1");
|
|||
|
|
MainTextBlock.SetBinding(TextBlock.TextProperty,
|
|||
|
|
new Binding("Title") { Source = this, Mode = BindingMode.OneWay });
|
|||
|
|
mainGrid.Children.Add(MainTextBlock);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (CanSwap || SwapControl is not null)
|
|||
|
|
{
|
|||
|
|
if (SwapControl is null && Children.Count > 3)
|
|||
|
|
SwapControl = Children[3];
|
|||
|
|
MainSwap = new Path
|
|||
|
|
{
|
|||
|
|
HorizontalAlignment = HorizontalAlignment.Right, Stretch = Stretch.Uniform, Height = 6d, Width = 10d,
|
|||
|
|
VerticalAlignment = VerticalAlignment.Top, Margin = new Thickness(0d, 17d, 16d, 0d),
|
|||
|
|
Data =
|
|||
|
|
(Geometry)new GeometryConverter().ConvertFromString("M2,4 l-2,2 10,10 10,-10 -2,-2 -8,8 -8,-8 z"),
|
|||
|
|
RenderTransform = new RotateTransform(180d), RenderTransformOrigin = new Point(0.5d, 0.5d)
|
|||
|
|
};
|
|||
|
|
MainSwap.SetResourceReference(Shape.FillProperty, "ColorBrush1");
|
|||
|
|
mainGrid.Children.Add(MainSwap);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 改变默认的折叠
|
|||
|
|
if (IsSwapped && SwapControl is not null)
|
|||
|
|
{
|
|||
|
|
MainSwap.RenderTransform = new RotateTransform(SwapLogoRight ? 270 : 0);
|
|||
|
|
SwapControl.Visibility = Visibility.Collapsed;
|
|||
|
|
// 取消由于高度变化被迫触发的高度动画
|
|||
|
|
var rawUseAnimation = UseAnimation;
|
|||
|
|
UseAnimation = false;
|
|||
|
|
Height = SwapedHeight;
|
|||
|
|
ModAnimation.AniStop("MyCard Height " + uuid);
|
|||
|
|
isHeightAnimating = false;
|
|||
|
|
ModBase.RunInUi(() => UseAnimation = rawUseAnimation, true);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void StackInstall()
|
|||
|
|
{
|
|||
|
|
var argstack = (StackPanel)SwapControl;
|
|||
|
|
StackInstall(ref argstack, InstallMethod);
|
|||
|
|
SwapControl = argstack;
|
|||
|
|
TriggerForceResize();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static void StackInstall(ref StackPanel stack, Action<StackPanel> installMethod)
|
|||
|
|
{
|
|||
|
|
if (stack.Tag is null)
|
|||
|
|
return;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
installMethod(stack);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(ex, "[MyCard] InstallMethod 调用失败");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
stack.Children.Add(new FrameworkElement { Height = 18d }); // 下边距,同时适应折叠
|
|||
|
|
stack.Tag = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void MyCard_MouseEnter(object sender, MouseEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (!HasMouseAnimation)
|
|||
|
|
return;
|
|||
|
|
var aniList = new List<ModAnimation.AniData>();
|
|||
|
|
if (MainTextBlock is not null)
|
|||
|
|
aniList.Add(ModAnimation.AaColor(MainTextBlock, TextBlock.ForegroundProperty, "ColorBrush2", 90));
|
|||
|
|
if (MainSwap is not null)
|
|||
|
|
aniList.Add(ModAnimation.AaColor(MainSwap, Shape.FillProperty, "ColorBrush2", 90));
|
|||
|
|
aniList.AddRange(new[]
|
|||
|
|
{
|
|||
|
|
ModAnimation.AaColor(MainChrome, MyDropShadow.ColorProperty, "ColorObject4", 90),
|
|||
|
|
ModAnimation.AaOpacity(MainChrome, dropShadowHoverOpacity - MainChrome.Opacity, 90)
|
|||
|
|
});
|
|||
|
|
if (!IsAnimating)
|
|||
|
|
ModAnimation.AniStart(aniList, "MyCard Mouse " + uuid);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void MyCard_MouseLeave(object sender, MouseEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (!HasMouseAnimation)
|
|||
|
|
return;
|
|||
|
|
var aniList = new List<ModAnimation.AniData>();
|
|||
|
|
if (MainTextBlock is not null)
|
|||
|
|
aniList.Add(ModAnimation.AaColor(MainTextBlock, TextBlock.ForegroundProperty, "ColorBrush1", 90));
|
|||
|
|
if (MainSwap is not null)
|
|||
|
|
aniList.Add(ModAnimation.AaColor(MainSwap, Shape.FillProperty, "ColorBrush1", 90));
|
|||
|
|
aniList.AddRange(new[]
|
|||
|
|
{
|
|||
|
|
ModAnimation.AaColor(MainChrome, MyDropShadow.ColorProperty, "ColorObject1", 90),
|
|||
|
|
ModAnimation.AaOpacity(MainChrome, dropShadowIdleOpacity - MainChrome.Opacity, 90)
|
|||
|
|
});
|
|||
|
|
if (!IsAnimating)
|
|||
|
|
ModAnimation.AniStart(aniList, "MyCard Mouse " + uuid);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region 高度改变动画
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 是否启用高度改变动画。
|
|||
|
|
/// </summary>
|
|||
|
|
public bool UseAnimation { get; set; } = true;
|
|||
|
|
|
|||
|
|
private bool isHeightAnimating;
|
|||
|
|
private double actualUsedHeight; // 回滚实际高度(例如 NaN)
|
|||
|
|
|
|||
|
|
private void MySizeChanged(object sender, SizeChangedEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (!UseAnimation)
|
|||
|
|
return;
|
|||
|
|
var deltaHeight = (IsSwapped ? SwapedHeight : e.NewSize.Height) - e.PreviousSize.Height;
|
|||
|
|
// 卡片的进入时动画已被页面通用切换动画替代
|
|||
|
|
if (e.PreviousSize.Height == 0d || isHeightAnimating || Math.Abs(deltaHeight) < 1d || ActualHeight == 0d)
|
|||
|
|
return;
|
|||
|
|
StartHeightAnimation(deltaHeight, e.PreviousSize.Height, false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 启动卡片高度变化的动画效果
|
|||
|
|
/// 根据变化距离的大小采用不同的动画策略:短距离使用简单缓动,长距离使用分段动画
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="delta">高度变化量</param>
|
|||
|
|
/// <param name="previousHeight">之前的高度</param>
|
|||
|
|
/// <param name="isLoadAnimation">是否为加载动画</param>
|
|||
|
|
private void StartHeightAnimation(double delta, double previousHeight, bool isLoadAnimation)
|
|||
|
|
{
|
|||
|
|
if (isHeightAnimating || ModMain.frmMain is null)
|
|||
|
|
return; // 避免 XAML 设计器出错
|
|||
|
|
|
|||
|
|
var animList = new List<ModAnimation.AniData>();
|
|||
|
|
var absDelta = Math.Abs(delta);
|
|||
|
|
|
|||
|
|
if (absDelta <= 800d)
|
|||
|
|
{
|
|||
|
|
// 短距离,直接使用 150ms 的缓动动画
|
|||
|
|
animList.Add(ModAnimation.AaHeight(this, delta, 150,
|
|||
|
|
ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.ExtraStrong)));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var easeLength = default(int);
|
|||
|
|
int easeTime;
|
|||
|
|
int initSpeed; // 到达缓动区前的初速度
|
|||
|
|
if (delta < 0d && absDelta - easeLength > 5000d * 0.1d)
|
|||
|
|
{
|
|||
|
|
// 收回距离过长 (>0.1s),强制以 100ms 完成匀速段,然后让减速段更长
|
|||
|
|
easeLength = 200;
|
|||
|
|
easeTime = 150;
|
|||
|
|
initSpeed = (int)Math.Round((absDelta - easeLength) / 0.1d);
|
|||
|
|
}
|
|||
|
|
else if (delta > 0d && absDelta - easeLength > 5000d * 0.6d)
|
|||
|
|
{
|
|||
|
|
// 展开距离过长 (>0.6s),以 5000 速度展示 300ms 匀速段,剩下的距离全部归入减速段
|
|||
|
|
initSpeed = 5000;
|
|||
|
|
easeLength = (int)Math.Round(absDelta - initSpeed * 0.3d);
|
|||
|
|
easeTime = 400;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 中程,匀速地快速展开(或收回)
|
|||
|
|
easeLength = 150;
|
|||
|
|
easeTime = 200;
|
|||
|
|
initSpeed = 4000;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 匀速段
|
|||
|
|
animList.Add(ModAnimation.AaHeight(this, (absDelta - easeLength) * Math.Sign(delta),
|
|||
|
|
(int)Math.Round((absDelta - easeLength) / initSpeed * 1000d)));
|
|||
|
|
// 减速段
|
|||
|
|
animList.Add(ModAnimation.AaHeight(this, easeLength * Math.Sign(delta), easeTime,
|
|||
|
|
ease: new ModAnimation.AniEaseOutFluentWithInitial(initSpeed, easeTime / 1000d, easeLength),
|
|||
|
|
after: true));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
animList.Add(ModAnimation.AaCode(() =>
|
|||
|
|
{
|
|||
|
|
isHeightAnimating = false;
|
|||
|
|
Height = actualUsedHeight;
|
|||
|
|
if (IsSwapped && SwapControl is not null)
|
|||
|
|
SwapControl.Visibility = Visibility.Collapsed;
|
|||
|
|
}, after: true));
|
|||
|
|
ModAnimation.AniStart(animList, "MyCard Height " + uuid);
|
|||
|
|
isHeightAnimating = true;
|
|||
|
|
actualUsedHeight = IsSwapped ? SwapedHeight : Height;
|
|||
|
|
Height = previousHeight;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 通知 MyCard,控件内容已改变,需要中断动画并瞬间更新高度。
|
|||
|
|
/// </summary>
|
|||
|
|
public void TriggerForceResize()
|
|||
|
|
{
|
|||
|
|
Height = IsSwapped ? SwapedHeight : double.NaN;
|
|||
|
|
ModAnimation.AniStop("MyCard Height " + uuid);
|
|||
|
|
isHeightAnimating = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 折叠
|
|||
|
|
|
|||
|
|
// 若设置了 CanSwap,或 SwapControl 不为空,则判定为会进行折叠
|
|||
|
|
// 这是因为不能直接在 XAML 中设置 SwapControl
|
|||
|
|
public UIElement SwapControl;
|
|||
|
|
public bool CanSwap { get; set; } = false;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 数据转为列表项的转换方法
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public Action<StackPanel> InstallMethod { get; set; }
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 是否已被折叠。
|
|||
|
|
/// </summary>
|
|||
|
|
public bool IsSwapped
|
|||
|
|
{
|
|||
|
|
get => field;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (field == value)
|
|||
|
|
return;
|
|||
|
|
field = value;
|
|||
|
|
if (SwapControl is null)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// 当卡片展开时,如果SwapControl是StackPanel类型,则执行安装方法
|
|||
|
|
// 这通常用于动态添加内容到折叠卡片中
|
|||
|
|
if (!IsSwapped && SwapControl is StackPanel)
|
|||
|
|
{
|
|||
|
|
var argstack = (StackPanel)SwapControl;
|
|||
|
|
StackInstall(ref argstack, InstallMethod);
|
|||
|
|
SwapControl = argstack;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 若尚未加载,会在 Loaded 事件中触发无动画的折叠,不需要在这里进行
|
|||
|
|
if (!IsLoaded)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// 更新控件的可见性和高度
|
|||
|
|
SwapControl.Visibility = Visibility.Visible;
|
|||
|
|
TriggerForceResize();
|
|||
|
|
|
|||
|
|
// 根据折叠状态旋转箭头图标
|
|||
|
|
// 折叠时箭头指向右侧或向上(根据SwapLogoRight设置),展开时指向下方
|
|||
|
|
ModAnimation.AniStart(
|
|||
|
|
ModAnimation.AaRotateTransform(MainSwap,
|
|||
|
|
(field ? SwapLogoRight ? 270 : 0 : 180) - ((RotateTransform)MainSwap.RenderTransform).Angle,
|
|||
|
|
250, ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.ExtraStrong)),
|
|||
|
|
"MyCard Swap " + uuid, true);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 是否已被折叠。(已过时,请使用 IsSwapped)
|
|||
|
|
/// </summary>
|
|||
|
|
[Obsolete("请使用 IsSwapped 属性,IsSwaped 存在拼写错误")]
|
|||
|
|
public bool IsSwaped
|
|||
|
|
{
|
|||
|
|
get => IsSwapped;
|
|||
|
|
set => IsSwapped = value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public bool SwapLogoRight { get; set; } = false;
|
|||
|
|
private bool isSwapMouseDown = false; //用于触发卡片展开/折叠的 MouseDown
|
|||
|
|
private bool isCustomMouseDown = false; //用于触发自定义事件的 MouseDown
|
|||
|
|
public event PreviewSwapEventHandler? PreviewSwap;
|
|||
|
|
|
|||
|
|
public delegate void PreviewSwapEventHandler(object sender, ModBase.RouteEventArgs e);
|
|||
|
|
|
|||
|
|
public event SwapEventHandler? Swap;
|
|||
|
|
|
|||
|
|
public delegate void SwapEventHandler(object sender, ModBase.RouteEventArgs e);
|
|||
|
|
|
|||
|
|
public const int SwapedHeight = 40;
|
|||
|
|
|
|||
|
|
private void MyCard_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|||
|
|
{
|
|||
|
|
double pos = Mouse.GetPosition(this).Y;
|
|||
|
|
if (!IsSwapped && (pos > (IsSwapped ? SwapedHeight : SwapedHeight - 6) || (pos == 0 && !IsMouseDirectlyOver)))
|
|||
|
|
return;
|
|||
|
|
isCustomMouseDown = true;
|
|||
|
|
if (!IsSwapped && (SwapControl is null || pos > (IsSwapped ? SwapedHeight : SwapedHeight - 6) || (pos == 0 && !IsMouseDirectlyOver)))
|
|||
|
|
return;
|
|||
|
|
isSwapMouseDown = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void MyCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (!isCustomMouseDown) return;
|
|||
|
|
isCustomMouseDown = false;
|
|||
|
|
ModMain.RaiseCustomEvent(this);
|
|||
|
|
|
|||
|
|
if (!isSwapMouseDown) return;
|
|||
|
|
isSwapMouseDown = false;
|
|||
|
|
|
|||
|
|
double pos = Mouse.GetPosition(this).Y;
|
|||
|
|
if (!IsSwapped && (SwapControl is null || pos > (IsSwapped ? SwapedHeight : SwapedHeight - 6) || (pos == 0 && !IsMouseDirectlyOver)))
|
|||
|
|
return; // 检测点击位置;或已经不在可视树上的误判
|
|||
|
|
|
|||
|
|
var e2 = new ModBase.RouteEventArgs(true);
|
|||
|
|
PreviewSwap?.Invoke(this, e2);
|
|||
|
|
if (e2.handled)
|
|||
|
|
{
|
|||
|
|
isSwapMouseDown = false;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IsSwapped = !IsSwapped;
|
|||
|
|
ModBase.Log("[Control] " + (IsSwapped ? "折叠卡片" : "展开卡片") + (Title is null ? "" : ":" + Title));
|
|||
|
|
Swap?.Invoke(this, e2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void MyCard_MouseLeave_Swap(object sender, MouseEventArgs e)
|
|||
|
|
{
|
|||
|
|
isSwapMouseDown = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static partial class ModAnimation
|
|||
|
|
{
|
|||
|
|
public static void AniDispose(MyCard control, bool removeFromChildren, ParameterizedThreadStart callBack = null)
|
|||
|
|
{
|
|||
|
|
if (control.IsHitTestVisible)
|
|||
|
|
{
|
|||
|
|
control.IsHitTestVisible = false;
|
|||
|
|
AniStart(new[]
|
|||
|
|
{
|
|||
|
|
AaScaleTransform(control, -0.08d, 200, ease: new AniEaseInFluent()),
|
|||
|
|
AaOpacity(control, -1, 200, ease: new AniEaseOutFluent()),
|
|||
|
|
AaHeight(control, -control.ActualHeight, 150, 100, new AniEaseOutFluent()),
|
|||
|
|
AaCode(() =>
|
|||
|
|
{
|
|||
|
|
if (removeFromChildren)
|
|||
|
|
{
|
|||
|
|
if (control.Parent is null)
|
|||
|
|
return;
|
|||
|
|
((Panel)control.Parent).Children.Remove(control);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
control.Visibility = Visibility.Collapsed;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (callBack is not null)
|
|||
|
|
callBack(control);
|
|||
|
|
}, after: true)
|
|||
|
|
}, "MyCard Dispose " + control.uuid);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
if (removeFromChildren)
|
|||
|
|
{
|
|||
|
|
if (control.Parent is null)
|
|||
|
|
return;
|
|||
|
|
((Panel)control.Parent).Children.Remove(control);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
control.Visibility = Visibility.Collapsed;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (callBack is not null)
|
|||
|
|
callBack(control);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|