我如何得到一个animation的gif在WPF中工作?
我应该使用哪种控制types – Image
, MediaElement
等?
我无法得到这个问题的最受欢迎的答案(以上由达里奥)正常工作。 结果是怪异的,动荡不安的古怪的文物。 迄今为止发现的最佳解决scheme: https : //github.com/XamlAnimatedGif/WpfAnimatedGif
您可以使用NuGet进行安装
PM> Install-Package WpfAnimatedGif
并使用它,在一个新的命名空间的窗口,你要添加的GIF图像,并使用它如下
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:gif="http://wpfanimatedgif.codeplex.com" <!-- THIS NAMESPACE --> Title="MainWindow" Height="350" Width="525"> <Grid> <!-- EXAMPLE USAGE BELOW --> <Image gif:ImageBehavior.AnimatedSource="Images/animated.gif" />
这个包真的很整洁,你可以设置一些如下的属性
<Image gif:ImageBehavior.RepeatBehavior="3x" gif:ImageBehavior.AnimatedSource="Images/animated.gif" />
你也可以在代码中使用它:
var image = new BitmapImage(); image.BeginInit(); image.UriSource = new Uri(fileName); image.EndInit(); ImageBehavior.SetAnimatedSource(img, image);
我发布了一个扩展图像控制和使用Gif解码器的解决scheme。 gif解码器有一个frames属性。 我为FrameIndex
属性设置了animation。 事件ChangingFrameIndex
将源属性ChangingFrameIndex
为与FrameIndex
(即解码器中)对应的帧。 我猜gif有每秒10帧。
class GifImage : Image { private bool _isInitialized; private GifBitmapDecoder _gifDecoder; private Int32Animation _animation; public int FrameIndex { get { return (int)GetValue(FrameIndexProperty); } set { SetValue(FrameIndexProperty, value); } } private void Initialize() { _gifDecoder = new GifBitmapDecoder(new Uri("pack://application:,,," + this.GifSource), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); _animation = new Int32Animation(0, _gifDecoder.Frames.Count - 1, new Duration(new TimeSpan(0, 0, 0, _gifDecoder.Frames.Count / 10, (int)((_gifDecoder.Frames.Count / 10.0 - _gifDecoder.Frames.Count / 10) * 1000)))); _animation.RepeatBehavior = RepeatBehavior.Forever; this.Source = _gifDecoder.Frames[0]; _isInitialized = true; } static GifImage() { VisibilityProperty.OverrideMetadata(typeof (GifImage), new FrameworkPropertyMetadata(VisibilityPropertyChanged)); } private static void VisibilityPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if ((Visibility)e.NewValue == Visibility.Visible) { ((GifImage)sender).StartAnimation(); } else { ((GifImage)sender).StopAnimation(); } } public static readonly DependencyProperty FrameIndexProperty = DependencyProperty.Register("FrameIndex", typeof(int), typeof(GifImage), new UIPropertyMetadata(0, new PropertyChangedCallback(ChangingFrameIndex))); static void ChangingFrameIndex(DependencyObject obj, DependencyPropertyChangedEventArgs ev) { var gifImage = obj as GifImage; gifImage.Source = gifImage._gifDecoder.Frames[(int)ev.NewValue]; } /// <summary> /// Defines whether the animation starts on it's own /// </summary> public bool AutoStart { get { return (bool)GetValue(AutoStartProperty); } set { SetValue(AutoStartProperty, value); } } public static readonly DependencyProperty AutoStartProperty = DependencyProperty.Register("AutoStart", typeof(bool), typeof(GifImage), new UIPropertyMetadata(false, AutoStartPropertyChanged)); private static void AutoStartPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) (sender as GifImage).StartAnimation(); } public string GifSource { get { return (string)GetValue(GifSourceProperty); } set { SetValue(GifSourceProperty, value); } } public static readonly DependencyProperty GifSourceProperty = DependencyProperty.Register("GifSource", typeof(string), typeof(GifImage), new UIPropertyMetadata(string.Empty, GifSourcePropertyChanged)); private static void GifSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { (sender as GifImage).Initialize(); } /// <summary> /// Starts the animation /// </summary> public void StartAnimation() { if (!_isInitialized) this.Initialize(); BeginAnimation(FrameIndexProperty, _animation); } /// <summary> /// Stops the animation /// </summary> public void StopAnimation() { BeginAnimation(FrameIndexProperty, null); } }
用法示例(XAML):
<controls:GifImage x:Name="gifImage" Stretch="None" GifSource="/SomeImage.gif" AutoStart="True" />
我也做了一个search,发现了几个不同的解决scheme,只是在旧的MSDN论坛的一个线程。 (链接不再工作,所以我删除了它)
最简单的执行似乎是使用WinForms PictureBox
控件,并像这样(从线程中改变了一些东西,大部分是相同的)。
首先将System.Windows.Forms
, WindowsFormsIntegration
和System.Drawing
的引用添加到您的项目中。
<Window x:Class="GifExample.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wfi="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration" xmlns:winForms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" Loaded="Window_Loaded" > <Grid> <wfi:WindowsFormsHost> <winForms:PictureBox x:Name="pictureBoxLoading"> </winForms:PictureBox> </wfi:WindowsFormsHost> </Grid> </Window >
然后在Window_Loaded
处理程序中,将pictureBoxLoading.ImageLocation
属性设置为要显示的图像文件path。
private void Window_Loaded(object sender, RoutedEventArgs e) { pictureBoxLoading.ImageLocation = "../Images/mygif.gif"; }
MediaElement
控件在那个线程中被提到,但是也提到它是一个相当重的控件,所以有很多的select,包括至less2个基于Image
控件的自制控件,所以这是最简单的。
这个小应用程序如何:代码背后:
public MainWindow() { InitializeComponent(); Files = Directory.GetFiles(@"I:\images"); this.DataContext= this; } public string[] Files {get;set;}
XAML:
<Window x:Class="PicViewer.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="175" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <ListBox x:Name="lst" ItemsSource="{Binding Path=Files}"/> <MediaElement Grid.Column="1" LoadedBehavior="Play" Source="{Binding ElementName=lst, Path=SelectedItem}" Stretch="None"/> </Grid> </Window>
这是我的animation图像控制版本。 您可以使用标准属性Source来指定图像源。 我进一步改进了它。 我是俄罗斯人,项目是俄罗斯,所以意见也是在俄罗斯。 但无论如何,你应该能够理解一切,没有评论。 🙂
/// <summary> /// Control the "Images", which supports animated GIF. /// </summary> public class AnimatedImage : Image { #region Public properties /// <summary> /// Gets / sets the number of the current frame. /// </summary> public int FrameIndex { get { return (int) GetValue(FrameIndexProperty); } set { SetValue(FrameIndexProperty, value); } } /// <summary> /// Gets / sets the image that will be drawn. /// </summary> public new ImageSource Source { get { return (ImageSource) GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } } #endregion #region Protected interface /// <summary> /// Provides derived classes an opportunity to handle changes to the Source property. /// </summary> protected virtual void OnSourceChanged(DependencyPropertyChangedEventArgs aEventArgs) { ClearAnimation(); BitmapImage lBitmapImage = aEventArgs.NewValue as BitmapImage; if (lBitmapImage == null) { ImageSource lImageSource = aEventArgs.NewValue as ImageSource; base.Source = lImageSource; return; } if (!IsAnimatedGifImage(lBitmapImage)) { base.Source = lBitmapImage; return; } PrepareAnimation(lBitmapImage); } #endregion #region Private properties private Int32Animation Animation { get; set; } private GifBitmapDecoder Decoder { get; set; } private bool IsAnimationWorking { get; set; } #endregion #region Private methods private void ClearAnimation() { if (Animation != null) { BeginAnimation(FrameIndexProperty, null); } IsAnimationWorking = false; Animation = null; Decoder = null; } private void PrepareAnimation(BitmapImage aBitmapImage) { Debug.Assert(aBitmapImage != null); if (aBitmapImage.UriSource != null) { Decoder = new GifBitmapDecoder( aBitmapImage.UriSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); } else { aBitmapImage.StreamSource.Position = 0; Decoder = new GifBitmapDecoder( aBitmapImage.StreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); } Animation = new Int32Animation( 0, Decoder.Frames.Count - 1, new Duration( new TimeSpan( 0, 0, 0, Decoder.Frames.Count / 10, (int) ((Decoder.Frames.Count / 10.0 - Decoder.Frames.Count / 10) * 1000)))) { RepeatBehavior = RepeatBehavior.Forever }; base.Source = Decoder.Frames[0]; BeginAnimation(FrameIndexProperty, Animation); IsAnimationWorking = true; } private bool IsAnimatedGifImage(BitmapImage aBitmapImage) { Debug.Assert(aBitmapImage != null); bool lResult = false; if (aBitmapImage.UriSource != null) { BitmapDecoder lBitmapDecoder = BitmapDecoder.Create( aBitmapImage.UriSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); lResult = lBitmapDecoder is GifBitmapDecoder; } else if (aBitmapImage.StreamSource != null) { try { long lStreamPosition = aBitmapImage.StreamSource.Position; aBitmapImage.StreamSource.Position = 0; GifBitmapDecoder lBitmapDecoder = new GifBitmapDecoder( aBitmapImage.StreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); lResult = lBitmapDecoder.Frames.Count > 1; aBitmapImage.StreamSource.Position = lStreamPosition; } catch { lResult = false; } } return lResult; } private static void ChangingFrameIndex (DependencyObject aObject, DependencyPropertyChangedEventArgs aEventArgs) { AnimatedImage lAnimatedImage = aObject as AnimatedImage; if (lAnimatedImage == null || !lAnimatedImage.IsAnimationWorking) { return; } int lFrameIndex = (int) aEventArgs.NewValue; ((Image) lAnimatedImage).Source = lAnimatedImage.Decoder.Frames[lFrameIndex]; lAnimatedImage.InvalidateVisual(); } /// <summary> /// Handles changes to the Source property. /// </summary> private static void OnSourceChanged (DependencyObject aObject, DependencyPropertyChangedEventArgs aEventArgs) { ((AnimatedImage) aObject).OnSourceChanged(aEventArgs); } #endregion #region Dependency Properties /// <summary> /// FrameIndex Dependency Property /// </summary> public static readonly DependencyProperty FrameIndexProperty = DependencyProperty.Register( "FrameIndex", typeof (int), typeof (AnimatedImage), new UIPropertyMetadata(0, ChangingFrameIndex)); /// <summary> /// Source Dependency Property /// </summary> public new static readonly DependencyProperty SourceProperty = DependencyProperty.Register( "Source", typeof (ImageSource), typeof (AnimatedImage), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, OnSourceChanged)); #endregion }
如果您使用<MediaElement>
它非常简单:
<MediaElement Height="113" HorizontalAlignment="Left" Margin="12,12,0,0" Name="mediaElement1" VerticalAlignment="Top" Width="198" Source="C:\Users\abc.gif" LoadedBehavior="Play" Stretch="Fill" SpeedRatio="1" IsMuted="False" />
我使用这个库: http : //wpfanimatedgif.codeplex.com/
首先,将库安装到您的项目中(使用软件包pipe理器控制台):
PM > Install-Package WpfAnimatedGif
然后,使用这个片段到XAML文件中:
<Window x:Class="WpfAnimatedGif.Demo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:gif="http://wpfanimatedgif.codeplex.com" Title="MainWindow" Height="350" Width="525"> <Grid> <Image gif:ImageBehavior.AnimatedSource="Images/animated.gif" /> ...
我希望有所帮助。
来源: http : //wpfanimatedgif.codeplex.com/
基本上与上面的PictureBox解决scheme相同,但是这次使用代码隐藏在项目中使用embedded式资源:
在XAML中:
<WindowsFormsHost x:Name="_loadingHost"> <Forms:PictureBox x:Name="_loadingPictureBox"/> </WindowsFormsHost>
在代码背后:
public partial class ProgressIcon { public ProgressIcon() { InitializeComponent(); var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("My.Namespace.ProgressIcon.gif"); var image = System.Drawing.Image.FromStream(stream); Loaded += (s, e) => _loadingPictureBox.Image = image; } }
我修改了Mike Eshva的代码,并且让它工作得更好。您可以将其与1frame jpg png bmp或mutil-frame gif一起使用。如果要将uri绑定到控件,请绑定UriSource属性或者要绑定任何in-内存stream,你绑定的Source属性是一个BitmapImage。
/// <summary> /// Элемент управления "Изображения", поддерживающий анимированные GIF. /// </summary> public class AnimatedImage : Image { static AnimatedImage() { DefaultStyleKeyProperty.OverrideMetadata(typeof(AnimatedImage), new FrameworkPropertyMetadata(typeof(AnimatedImage))); } #region Public properties /// <summary> /// Получает/устанавливает номер текущего кадра. /// </summary> public int FrameIndex { get { return (int)GetValue(FrameIndexProperty); } set { SetValue(FrameIndexProperty, value); } } /// <summary> /// Get the BitmapFrame List. /// </summary> public List<BitmapFrame> Frames { get; private set; } /// <summary> /// Get or set the repeatBehavior of the animation when source is gif formart.This is a dependency object. /// </summary> public RepeatBehavior AnimationRepeatBehavior { get { return (RepeatBehavior)GetValue(AnimationRepeatBehaviorProperty); } set { SetValue(AnimationRepeatBehaviorProperty, value); } } public new BitmapImage Source { get { return (BitmapImage)GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } } public Uri UriSource { get { return (Uri)GetValue(UriSourceProperty); } set { SetValue(UriSourceProperty, value); } } #endregion #region Protected interface /// <summary> /// Provides derived classes an opportunity to handle changes to the Source property. /// </summary> protected virtual void OnSourceChanged(DependencyPropertyChangedEventArgs e) { ClearAnimation(); BitmapImage source; if (e.NewValue is Uri) { source = new BitmapImage(); source.BeginInit(); source.UriSource = e.NewValue as Uri; source.CacheOption = BitmapCacheOption.OnLoad; source.EndInit(); } else if (e.NewValue is BitmapImage) { source = e.NewValue as BitmapImage; } else { return; } BitmapDecoder decoder; if (source.StreamSource != null) { decoder = BitmapDecoder.Create(source.StreamSource, BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnLoad); } else if (source.UriSource != null) { decoder = BitmapDecoder.Create(source.UriSource, BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnLoad); } else { return; } if (decoder.Frames.Count == 1) { base.Source = decoder.Frames[0]; return; } this.Frames = decoder.Frames.ToList(); PrepareAnimation(); } #endregion #region Private properties private Int32Animation Animation { get; set; } private bool IsAnimationWorking { get; set; } #endregion #region Private methods private void ClearAnimation() { if (Animation != null) { BeginAnimation(FrameIndexProperty, null); } IsAnimationWorking = false; Animation = null; this.Frames = null; } private void PrepareAnimation() { Animation = new Int32Animation( 0, this.Frames.Count - 1, new Duration( new TimeSpan( 0, 0, 0, this.Frames.Count / 10, (int)((this.Frames.Count / 10.0 - this.Frames.Count / 10) * 1000)))) { RepeatBehavior = RepeatBehavior.Forever }; base.Source = this.Frames[0]; BeginAnimation(FrameIndexProperty, Animation); IsAnimationWorking = true; } private static void ChangingFrameIndex (DependencyObject dp, DependencyPropertyChangedEventArgs e) { AnimatedImage animatedImage = dp as AnimatedImage; if (animatedImage == null || !animatedImage.IsAnimationWorking) { return; } int frameIndex = (int)e.NewValue; ((Image)animatedImage).Source = animatedImage.Frames[frameIndex]; animatedImage.InvalidateVisual(); } /// <summary> /// Handles changes to the Source property. /// </summary> private static void OnSourceChanged (DependencyObject dp, DependencyPropertyChangedEventArgs e) { ((AnimatedImage)dp).OnSourceChanged(e); } #endregion #region Dependency Properties /// <summary> /// FrameIndex Dependency Property /// </summary> public static readonly DependencyProperty FrameIndexProperty = DependencyProperty.Register( "FrameIndex", typeof(int), typeof(AnimatedImage), new UIPropertyMetadata(0, ChangingFrameIndex)); /// <summary> /// Source Dependency Property /// </summary> public new static readonly DependencyProperty SourceProperty = DependencyProperty.Register( "Source", typeof(BitmapImage), typeof(AnimatedImage), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, OnSourceChanged)); /// <summary> /// AnimationRepeatBehavior Dependency Property /// </summary> public static readonly DependencyProperty AnimationRepeatBehaviorProperty = DependencyProperty.Register( "AnimationRepeatBehavior", typeof(RepeatBehavior), typeof(AnimatedImage), new PropertyMetadata(null)); public static readonly DependencyProperty UriSourceProperty = DependencyProperty.Register( "UriSource", typeof(Uri), typeof(AnimatedImage), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, OnSourceChanged)); #endregion }
这是一个自定义控件。 您需要在WPF App Project中创build它,并删除样式中的模板覆盖。
我有这个问题,直到我发现在WPF4中,你可以模拟你自己的关键帧图像animation。 首先,将您的animation分成一系列图像,标题为“Image1.gif”,“Image2,gif”等。 将这些图像导入您的解决scheme资源。 我假设你把它们放在图像的默认资源位置。
您将使用图像控件。 使用以下XAML代码。 我已经删除了非必需品。
<Image Name="Image1"> <Image.Triggers> <EventTrigger RoutedEvent="Image.Loaded" <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <ObjectAnimationUsingKeyFrames Duration="0:0:1" Storyboard.TargetProperty="Source" RepeatBehavior="Forever"> <DiscreteObjectKeyFrames KeyTime="0:0:0"> <DiscreteObjectKeyFrame.Value> <BitmapImage UriSource="Images/Image1.gif"/> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrames> <DiscreteObjectKeyFrames KeyTime="0:0:0.25"> <DiscreteObjectKeyFrame.Value> <BitmapImage UriSource="Images/Image2.gif"/> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrames> <DiscreteObjectKeyFrames KeyTime="0:0:0.5"> <DiscreteObjectKeyFrame.Value> <BitmapImage UriSource="Images/Image3.gif"/> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrames> <DiscreteObjectKeyFrames KeyTime="0:0:0.75"> <DiscreteObjectKeyFrame.Value> <BitmapImage UriSource="Images/Image4.gif"/> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrames> <DiscreteObjectKeyFrames KeyTime="0:0:1"> <DiscreteObjectKeyFrame.Value> <BitmapImage UriSource="Images/Image5.gif"/> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrames> </ObjectAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Image.Triggers> </Image>
感谢您的post乔尔,它帮助我解决了WPF缺less对animationGIF的支持。 只需添加一些代码,因为我有一个时间设置pictureBoxLoading.Image属性由于Winforms api。
我必须设置我的animationgif图像的生成行动为“内容”和复制到输出目录“复制如果更新”或“总是”。 然后在MainWindow()中我调用了这个方法。 唯一的问题是,当我试图处理stream,它给了我一个红包graphics,而不是我的形象。 我必须解决这个问题。 这消除了加载一个BitmapImage,并将其更改为一个位图(显然因为它不再是一个gif而杀死了我的animation)的痛苦。
private void SetupProgressIcon() { Uri uri = new Uri("pack://application:,,,/WPFTest;component/Images/animated_progress_apple.gif"); if (uri != null) { Stream stream = Application.GetContentStream(uri).Stream; imgProgressBox.Image = new System.Drawing.Bitmap(stream); } }
我一直在尝试以上,但每个人都有他们的简短,并感谢所有你,我制定了我自己的GifImage:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Controls; using System.Windows; using System.Windows.Media.Imaging; using System.IO; using System.Windows.Threading; namespace IEXM.Components { public class GifImage : Image { #region gif Source, such as "/IEXM;component/Images/Expression/f020.gif" public string GifSource { get { return (string)GetValue(GifSourceProperty); } set { SetValue(GifSourceProperty, value); } } public static readonly DependencyProperty GifSourceProperty = DependencyProperty.Register("GifSource", typeof(string), typeof(GifImage), new UIPropertyMetadata(null, GifSourcePropertyChanged)); private static void GifSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { (sender as GifImage).Initialize(); } #endregion #region control the animate /// <summary> /// Defines whether the animation starts on it's own /// </summary> public bool IsAutoStart { get { return (bool)GetValue(AutoStartProperty); } set { SetValue(AutoStartProperty, value); } } public static readonly DependencyProperty AutoStartProperty = DependencyProperty.Register("IsAutoStart", typeof(bool), typeof(GifImage), new UIPropertyMetadata(false, AutoStartPropertyChanged)); private static void AutoStartPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) (sender as GifImage).StartAnimation(); else (sender as GifImage).StopAnimation(); } #endregion private bool _isInitialized = false; private System.Drawing.Bitmap _bitmap; private BitmapSource _source; [System.Runtime.InteropServices.DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject); private BitmapSource GetSource() { if (_bitmap == null) { _bitmap = new System.Drawing.Bitmap(Application.GetResourceStream( new Uri(GifSource, UriKind.RelativeOrAbsolute)).Stream); } IntPtr handle = IntPtr.Zero; handle = _bitmap.GetHbitmap(); BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); DeleteObject(handle); return bs; } private void Initialize() { // Console.WriteLine("Init: " + GifSource); if (GifSource != null) Source = GetSource(); _isInitialized = true; } private void FrameUpdatedCallback() { System.Drawing.ImageAnimator.UpdateFrames(); if (_source != null) { _source.Freeze(); } _source = GetSource(); // Console.WriteLine("Working: " + GifSource); Source = _source; InvalidateVisual(); } private void OnFrameChanged(object sender, EventArgs e) { Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(FrameUpdatedCallback)); } /// <summary> /// Starts the animation /// </summary> public void StartAnimation() { if (!_isInitialized) this.Initialize(); // Console.WriteLine("Start: " + GifSource); System.Drawing.ImageAnimator.Animate(_bitmap, OnFrameChanged); } /// <summary> /// Stops the animation /// </summary> public void StopAnimation() { _isInitialized = false; if (_bitmap != null) { System.Drawing.ImageAnimator.StopAnimate(_bitmap, OnFrameChanged); _bitmap.Dispose(); _bitmap = null; } _source = null; Initialize(); GC.Collect(); GC.WaitForFullGCComplete(); // Console.WriteLine("Stop: " + GifSource); } public void Dispose() { _isInitialized = false; if (_bitmap != null) { System.Drawing.ImageAnimator.StopAnimate(_bitmap, OnFrameChanged); _bitmap.Dispose(); _bitmap = null; } _source = null; GC.Collect(); GC.WaitForFullGCComplete(); // Console.WriteLine("Dispose: " + GifSource); } } }
用法:
<localComponents:GifImage x:Name="gifImage" IsAutoStart="True" GifSource="{Binding Path=value}" />
因为它不会导致内存泄漏,它animationgif图像自己的时间线,你可以尝试。
以前,我面临类似的问题,我需要在您的项目中播放.gif
文件。 我有两个select:
-
使用WinForms中的PictureBox
-
使用第三方库,如codeplex.com的 WPFAnimatedGif 。
带有PictureBox
版本不适用于我,而且该项目无法使用外部库。 所以我通过Bitmap
帮助ImageAnimator
为自己ImageAnimator
。 因为标准的BitmapImage
不支持.gif
文件的播放。
完整的例子:
XAML
<Window x:Class="PlayGifHelp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" Loaded="MainWindow_Loaded"> <Grid> <Image x:Name="SampleImage" /> </Grid> </Window>
Code behind
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } Bitmap _bitmap; BitmapSource _source; private BitmapSource GetSource() { if (_bitmap == null) { string path = Directory.GetCurrentDirectory(); // Check the path to the .gif file _bitmap = new Bitmap(path + @"\anim.gif"); } IntPtr handle = IntPtr.Zero; handle = _bitmap.GetHbitmap(); return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { _source = GetSource(); SampleImage.Source = _source; ImageAnimator.Animate(_bitmap, OnFrameChanged); } private void FrameUpdatedCallback() { ImageAnimator.UpdateFrames(); if (_source != null) { _source.Freeze(); } _source = GetSource(); SampleImage.Source = _source; InvalidateVisual(); } private void OnFrameChanged(object sender, EventArgs e) { Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(FrameUpdatedCallback)); } }
Bitmap
does not support URI directive, so I load .gif
file from the current directory.
Small improvement of GifImage.Initialize()
method, which reads proper frame timing from GIF metadata.
private void Initialize() { _gifDecoder = new GifBitmapDecoder(new Uri("pack://application:,,," + this.GifSource), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); int duration=0; _animation = new Int32AnimationUsingKeyFrames(); _animation.KeyFrames.Add(new DiscreteInt32KeyFrame(0, KeyTime.FromTimeSpan(new TimeSpan(0)))); foreach (BitmapFrame frame in _gifDecoder.Frames) { BitmapMetadata btmd = (BitmapMetadata)frame.Metadata; duration += (ushort)btmd.GetQuery("/grctlext/Delay"); _animation.KeyFrames.Add(new DiscreteInt32KeyFrame(_gifDecoder.Frames.IndexOf(frame)+1, KeyTime.FromTimeSpan(new TimeSpan(duration*100000)))); } _animation.RepeatBehavior = RepeatBehavior.Forever; this.Source = _gifDecoder.Frames[0]; _isInitialized = true; }
I am not sure if this has been solved but the best way is to use the WpfAnimatedGid library . It is very easy, simple and straight forward to use. It only requires 2lines of XAML code and about 5 lines of C# Code in the code behind.
You will see all the necessary details of how this can be used there. This is what I also used instead of re-inventing the wheel