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

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

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

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

    Here is my version of animated image control. You can use standard property Source for specifying image source. I further improved it. I am a russian, project is russian so comments are also in Russian. But anyway you should be able understand everything without comments. :)

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

    Previously, I faced a similar problem, I needed to play .gif file in your project. I had two choices:

    • using PictureBox from WinForms

    • using a third-party library, such as WPFAnimatedGif from codeplex.com.

    Version with PictureBox did not work for me, and the project could not use external libraries for it. So I made it for myself through Bitmap with help ImageAnimator. Because, standard BitmapImage does not support playback of .gif files.

    Full example:

    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.

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

    How about this tiny app: Code behind:

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

    An alternative to waiting animation in WPF is:

     <ProgressBar Height="20" Width="100" IsIndeterminate="True"/>
    

    It will show an animated progress bar.

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

    I use this library: https://github.com/XamlAnimatedGif/WpfAnimatedGif

    First, install library into your project (using Package Manager Console):

        PM > Install-Package WpfAnimatedGif
    

    Then, use this snippet into XAML file:

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

    I hope helps.

    Source: https://github.com/XamlAnimatedGif/WpfAnimatedGif

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

    I modified Mike Eshva's code,And I made it work better.You can use it with either 1frame jpg png bmp or mutil-frame gif.If you want bind a uri to the control,bind the UriSource properties or you want bind any in-memory stream that you bind the Source propertie which is a 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
    }
    

    This is a custom control. You need to create it in WPF App Project,and delete the Template override in style.

    0 讨论(0)
提交回复
热议问题