How do I get an animated gif to work in WPF?

前端 未结 18 934
广开言路
广开言路 2020-11-22 12:16

What control type should I use - Image, MediaElement, etc.?

相关标签:
18条回答
  • 2020-11-22 12:18

    Its very simple if you use <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" />
    
    0 讨论(0)
  • 2020-11-22 12:20

    I post a solution extending the image control and using the Gif Decoder. The gif decoder has a frames property. I animate the FrameIndex property. The event ChangingFrameIndex changes the source property to the frame corresponding to the FrameIndex (that is in the decoder). I guess that the gif has 10 frames per second.

    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);
        }
    }
    

    Usage example (XAML):

    <controls:GifImage x:Name="gifImage" Stretch="None" GifSource="/SomeImage.gif" AutoStart="True" />
    
    0 讨论(0)
  • 2020-11-22 12:21

    I couldn't get the most popular answer to this question (above by Dario) to work properly. The result was weird, choppy animation with weird artifacts. Best solution I have found so far: https://github.com/XamlAnimatedGif/WpfAnimatedGif

    You can install it with NuGet

    PM> Install-Package WpfAnimatedGif

    and to use it, at a new namespace to the Window where you want to add the gif image and use it as below

    <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" />
    

    The package is really neat, you can set some attributes like below

    <Image gif:ImageBehavior.RepeatBehavior="3x"
           gif:ImageBehavior.AnimatedSource="Images/animated.gif" />
    

    and you can use it in your code as well:

    var image = new BitmapImage();
    image.BeginInit();
    image.UriSource = new Uri(fileName);
    image.EndInit();
    ImageBehavior.SetAnimatedSource(img, image);
    

    EDIT: Silverlight support

    As per josh2112's comment if you want to add animated GIF support to your Silverlight project then use github.com/XamlAnimatedGif/XamlAnimatedGif

    0 讨论(0)
  • 2020-11-22 12:22

    I have try all the way above, but each one has their shortness, and thanks to all you, I work out my own 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);
                }
        }
    }
    

    Usage:

    <localComponents:GifImage x:Name="gifImage" IsAutoStart="True" GifSource="{Binding Path=value}" />
    

    As it would not cause memory leak and it animated the gif image own time line, you can try it.

    0 讨论(0)
  • 2020-11-22 12:23

    Thanks for your post Joel, it helped me solve WPF's absence of support for animated GIFs. Just adding a little code since I had a heck of a time with setting the pictureBoxLoading.Image property due to the Winforms api.

    I had to set my animated gif image's Build Action as "Content" and the Copy to output directory to "Copy if newer" or "always". Then in the MainWindow() I called this method. Only issue is that when I tried to dispose of the stream, it gave me a red envelope graphic instead of my image. I'll have to solve that problem. This removed the pain of loading a BitmapImage and changing it into a Bitmap (which obviously killed my animation because it is no longer a gif).

    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);
       }
    }
    
    0 讨论(0)
  • 2020-11-22 12:23

    Adding on to the main response that recommends the usage of WpfAnimatedGif, you must add the following lines in the end if you are swapping an image with a Gif to ensure the animation actually executes:

    ImageBehavior.SetRepeatBehavior(img, new RepeatBehavior(0));
    ImageBehavior.SetRepeatBehavior(img, RepeatBehavior.Forever);
    

    So your code will look like:

    var image = new BitmapImage();
    image.BeginInit();
    image.UriSource = new Uri(fileName);
    image.EndInit();
    ImageBehavior.SetAnimatedSource(img, image);
    ImageBehavior.SetRepeatBehavior(img, new RepeatBehavior(0));
    ImageBehavior.SetRepeatBehavior(img, RepeatBehavior.Forever);
    
    0 讨论(0)
提交回复
热议问题