Files
ShrlAlgoToolkit/WPFluent/Controls/AnimatedImage/AnimatedImage.cs
2025-04-24 20:56:44 +08:00

131 lines
3.6 KiB
C#

using System;
using System.IO;
using System.Linq;
namespace WPFluent.Controls;
public class AnimatedImage : Image, IDisposable
{
public static List<KeyValuePair<string[], Type>> Providers { get; } = [];
private AnimationProvider _animation = null!;
private bool _disposing;
static AnimatedImage()
{
Providers.Add(
new KeyValuePair<string[], Type>(
[".apng", ".png", ".jpg", ".jpeg", ".bmp"],
typeof(BitmapImageProvider)));
}
public void Dispose()
{
_disposing = true;
BeginAnimation(AnimationFrameIndexProperty, null);
Source = null;
_animation?.Dispose();
_animation = null!;
}
public event EventHandler? ImageLoaded;
public event EventHandler? DoZoomToFit;
private static AnimationProvider InitAnimationProvider(Uri path)
{
var ext = Path.GetExtension(path.LocalPath).ToLower();
var type = Providers.First(p => p.Key.Contains(ext) || p.Key.Contains("*")).Value;
var provider = type.CreateInstance<AnimationProvider>(path);
return provider;
}
public static readonly DependencyProperty AnimationFrameIndexProperty =
DependencyProperty.Register(nameof(AnimationFrameIndex), typeof(int), typeof(AnimatedImage), new UIPropertyMetadata(-1, AnimationFrameIndexChanged));
public static readonly DependencyProperty AnimationUriProperty =
DependencyProperty.Register(nameof(AnimationUri), typeof(Uri), typeof(AnimatedImage), new UIPropertyMetadata(null, AnimationUriChanged));
public int AnimationFrameIndex
{
get => (int)GetValue(AnimationFrameIndexProperty);
set => SetValue(AnimationFrameIndexProperty, value);
}
public Uri AnimationUri
{
get => (Uri)GetValue(AnimationUriProperty);
set => SetValue(AnimationUriProperty, value);
}
private static void AnimationUriChanged(DependencyObject obj, DependencyPropertyChangedEventArgs ev)
{
if (obj is not AnimatedImage instance)
{
return;
}
instance._animation = InitAnimationProvider((Uri)ev.NewValue);
ShowThumbnailAndStartAnimation(instance);
}
private static void ShowThumbnailAndStartAnimation(AnimatedImage instance)
{
instance.Dispatcher.Invoke(() =>
{
if (instance._disposing)
{
return;
}
instance.DoZoomToFit?.Invoke(instance, new EventArgs());
instance.ImageLoaded?.Invoke(instance, new EventArgs());
instance.BeginAnimation(AnimationFrameIndexProperty, instance._animation?.Animator);
});
}
private static void AnimationFrameIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs ev)
{
if (obj is not AnimatedImage instance)
{
return;
}
if (instance._disposing)
{
return;
}
var task = instance._animation.GetRenderedFrame((int)ev.NewValue);
task.ContinueWith(_ => instance.Dispatcher.Invoke(() =>
{
if (instance._disposing)
return;
var firstLoad = instance.Source == null;
instance.Source = _.Result;
if (firstLoad)
{
instance.DoZoomToFit?.Invoke(instance, new EventArgs());
instance.ImageLoaded?.Invoke(instance, new EventArgs());
}
}));
}
}
file static class TypeExtensions
{
public static T CreateInstance<T>(this Type t, params object[] paramArray)
{
return (T)Activator.CreateInstance(t, paramArray)!;
}
}