245

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

Marius Bancila
  • 16,053
  • 9
  • 49
  • 91
ironpaw
  • 2,995
  • 3
  • 19
  • 10
  • 6
    Here is a recent summary of the below solutions. I implemented these using VS2015. The GifImage class submitted by Dario worked great, but some of my gifs were artifacted. The MediaElement approach by Pradip Daunde and nicael seems to work in the preview area, but none of my gifs rendered during runtime. The WpfAnimatedGif solution by IgorVaschuk and SaiyanGirl worked great without issues but required installing a third party library (obviously). I didn't try out the rest. – Heath Carroll Feb 14 '16 at 19:08

21 Answers21

244

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

ASh
  • 34,632
  • 9
  • 60
  • 82
Igor Vaschuk
  • 2,794
  • 1
  • 19
  • 11
  • 14
    This worked great, and took less than 60 seconds to implement. Thanks! – Ryan Sorensen Aug 16 '12 at 23:02
  • 3
    Way better answer than any of the popular ones IMO, especially since it doesn't rely on you using C# – Jamie E Aug 13 '13 at 23:50
  • 11
    This is so much better than the accepted answer: uses the gif's metadata, isn't choppy, is a NuGet package, is language agnostic. I wish stackoverflow allowed for a vote of no confidence in the accepted answer. – John Gietzen Oct 13 '13 at 20:57
  • 1
    No matter what Animated Source I use I get a System.IO.FileNotFoundException, even if the image can load it fine. Is anyone else experiencing this? – Jason Jul 12 '15 at 21:53
  • 7
    Public service announcement: The author of WpfAnimatedGif has 'rebooted' his project as XamlAnimatedGif, and it supports WPF, Windows Store (Win8), Windows 10, and Silverlight: https://github.com/XamlAnimatedGif/XamlAnimatedGif – josh2112 Feb 09 '16 at 19:14
  • @Jason are you able to solve the exception? I'm getting the same error too – Andrei dela Cruz Mar 03 '16 at 03:09
  • @ironpaw, this solution worked perfect for me. I think it deserves to be marked as answer. – Djuka Mar 03 '17 at 11:37
  • Can I make an event of click? – Raz Luvaton Jul 09 '17 at 20:14
  • What if you want to repeat indefinitely? – soulblazer Apr 27 '18 at 02:27
  • 5
    What is `img` here? – amit jha Nov 20 '18 at 08:59
  • I tried using the same.. And I'm getting COM Exception : Insufficient memory. For my project the GIF can be of more than a minute's duration. And the RenderTargetBitmap used in ImageBehavior is causing it. Did you face the same issue? – shruti singh Dec 11 '18 at 05:59
  • @josh2112 Thanks a ton for your 'announcement', I was frustrated in finding the Silverlight version of the DLL – Saumil Mar 26 '19 at 14:44
  • Getting `Provide value on 'System.Windows.Baml2006.TypeConverterMarkupExtension` – The Muffin Man May 13 '19 at 22:03
  • @soulblazer gif:ImageBehavior.RepeatBehavior="Forever" to make it work forever – Dr. Freddy Dimethyltryptamine May 14 '20 at 17:05
  • I tried it and it works, but the memory usage is unusably high. 900+MB just for a GIF image of size 3.7MB. Windows builtin photos app doesn't even take a 100mb. Even my android app displaying the same image only takes 20mb of ram. – saksham Jun 22 '21 at 14:33
  • To add a bit to the answer: if your use case is showing "traditional" and gif images in the same `Image` control after each other, just call `ImageBehavior.SetAnimatedSource(myImageControl, null);` before loading a jpg/png/bmp/etc. into the control, otherwise it'll keep showing the gif image. – sisisisi Sep 18 '21 at 20:54
114

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" />
Marius Bancila
  • 16,053
  • 9
  • 49
  • 91
  • 3
    This one works, and better for XBAP apps, because you don't need additional references. – Massimiliano Dec 14 '09 at 16:24
  • 1
    That's cool. By putting your constructor code in the "Initialized" event and introducing a Uri property, this control can also be placed in a XAML file. – flq Oct 09 '10 at 09:37
  • 1
    +1, nice one ! However, it doesn't take the image's actual frame duration into account... If you can find a way to read that information, you could change the code to use an `Int32AnimationUsingKeyFrames` – Thomas Levesque Dec 10 '10 at 23:06
  • 7
    Actually, framerate is constant for GIF, so you don't need keyframes after all... You can read the framerate with `gf.Frames[0].MetaData.GetQuery("/grctlext/Delay")` (returns a ushort which is the frame duration in hundreds of second) – Thomas Levesque Dec 10 '10 at 23:40
  • how do you declare it in xaml? looks like constructor needs Uri? – Sonic Soul Nov 12 '11 at 21:20
  • 1
    Small update to @ThomasLevesque comment. The code line must look like: gf.Frames[0].Metadata.GetQuery("/grctlext/Delay") – Seekeer Nov 24 '11 at 14:29
  • The best solution I could fine is here! – NoWar Feb 09 '12 at 17:13
  • 1
    @ThomasLevesque actually, framerate can very over time for GIFs. Please read the GIF 89a specification before answering to this comment. – vidstige Oct 29 '12 at 14:06
  • 3
    @vidstige, yes, I don't remember why I had made this comment at the time (almost 2 years ago). I'm aware that the delay can be different for each frame, and my [WPF Animated GIF](http://wpfanimatedgif.codeplex.com/) library properly takes it into account. – Thomas Levesque Oct 29 '12 at 21:58
  • 2
    I tried to implement the above solution. The GIF is choppy and loses color. – user3260977 Jul 03 '15 at 15:40
  • 1
    Small update to @Seekeer @ThomasLevesque. The code line must look like: `((BitmapMetadata)gf.Frames[0]).GetQuery("/grctlext/Delay")` and Freeze the frame.see:https://stackoverflow.com/a/4589103/6116637 – lindexi Oct 18 '17 at 09:48
  • I am new to wpf, i tried your solution but it gives me an erroe `The namespace Controls is not defined` – Software Dev Dec 08 '17 at 06:13
  • I get "The UI prefix is not recognized" in the line _gifDecoder = new GifBitmapDecoder(new Uri...... – baye dbest Jan 18 '19 at 03:14
  • I get these errors `Image does not contain a constructor that takes 0 arguments`, `The name GetValue does not exist in the context `. same for `VisibilityProperty`. what am I doing wrong ?? – mrid May 31 '19 at 12:29
42

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>
MrPloops
  • 421
  • 4
  • 2
  • 1
    Nice ! Short code, doing the job well. I can't believe it does not have more upvotes. – wip Aug 09 '13 at 02:19
  • 2
    Best answer... Should be at the top! I was able to get it working without any code behind -- just this `` -- The MyGifFile is just the file name (and path) of my animated gif. – Anthony Nichols May 27 '16 at 17:14
  • Jeez, why even bother to bind to the `ListBox`, or bind at all? I tried it without binding, just put the file path in the Source and it appears, but does not animate. If I use binding, even with the `ListBox`, it does not come up at all, for me - it will give me an exception that my file path is incorrect, even though it's the same one I use for when it appears. – vapcguy Oct 17 '16 at 21:53
  • 1
    It takes to long to update and needs to be updated every time it goes into view. – Yola Jan 24 '19 at 10:31
  • Didn't work right for me. My gif is a rotating cube. It shows all the frames in the gif so the corners of the cube show up from all the frames in the background of the one in focus. If the gif frames were all the same shape (like a rotating circle, then it would work ok. – stymie2 Jun 24 '22 at 14:21
39

I, too, did a search and found several different solution in just a thread on the old MSDN forums. (link no longer worked so I removed it)

The simplest to execute seems to be to use a WinForms PictureBox control, and went like this (changed a few things from the thread, most of it the same).

Add a reference to System.Windows.Forms, WindowsFormsIntegration, and System.Drawing to your project first.

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

Then in the Window_Loaded handler, you would set the pictureBoxLoading.ImageLocation property to the image file path that you want to show.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    pictureBoxLoading.ImageLocation = "../Images/mygif.gif";
}

The MediaElement control was mentioned in that thread, but it is also mentioned that it is a rather heavy control, so there were a number of alternatives, including at least 2 homebrewed controls based on the Image control, so this is the simplest.

vapcguy
  • 7,097
  • 1
  • 56
  • 52
Joel B Fant
  • 24,406
  • 4
  • 66
  • 67
  • can you put this main window with AllowTransparency="True" when using WindowsFormsHost? – Junior Mayhé Dec 14 '09 at 19:31
  • @Junior: Yeah, you can set `AllowTransparency="True"`. Whether or not that will produce the results you have in mind is another matter. I haven't tried it, myself, but I would bet that the `WindowsFormsHost` would not become transparent at all. The rest of the `Window` might. You'll simply have to try it, I think. – Joel B Fant Dec 15 '09 at 18:58
  • I had trouble with the pictureBoxLoading.Image due to the winform API. I posted code below that solved my problem. Thanks for your solution, Joel! – sondlerd Jun 29 '11 at 18:56
  • Looks like your like is dead. Was it [this thread](http://social.msdn.microsoft.com/Forums/vstudio/en-US/9fa8a2e5-3360-4463-ab4b-a2db8b4d7bea/how-to-make-animated-gif-image-move-in-picturebox)? – wip Aug 09 '13 at 01:39
  • @wil: No, as it doesn't mention `MediaElement`. Thanks for the notice on the link. I'm surprised it lasted as long as it did. – Joel B Fant Aug 09 '13 at 14:59
  • 2
    When adding Integration reference, its name in my UI is WindowsFormsIntegration, without dot: http://i.imgur.com/efMiC23.png – yu yang Jian Jun 22 '16 at 03:10
  • Neglected to mention you also have to reference `System.Drawing`. And it's not the `.Image` property you set, but you set the `.ImageLocation` if you are directing it at the file location, directly. Finally got it to work, but this answer should have included these details. I submitted an edit. – vapcguy Oct 17 '16 at 22:17
  • I found I can't apply a height/width to the `PictureBox` : `Index was outside the bounds of the array`, unless you also apply the same dimensions to the `WindowsFormsHost` and `Grid` or `StackPanel` that it resides in. – vapcguy Oct 17 '16 at 22:25
  • Well, to revise my previous comment - the `WindowsFormHost` should have the same or larger dimensions than the images contained within, and the `StackPanel` or `Grid` has to have equal or larger dimensions than the `WindowsFormHost`. You could have multiple `WindowsFormHost` containers in a `StackPanel` or `Grid`, but their combined dimensions have to be less than or equal to the dimensions of the `StackPanel` or `Grid` they are contained in. Ex. if my WFH has `Height=100`, my `StackPanel` or `Grid` height must be 100 or more. If I add another WFH, I have to adjust so both equal 100, total. – vapcguy Oct 18 '16 at 19:05
  • There is a problem with rendering. – Yola Jan 24 '19 at 10:31
20

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" />
nicael
  • 18,550
  • 13
  • 57
  • 90
Pradip Daunde
  • 381
  • 2
  • 5
  • Just in case your file is packaged up in your app you can use DataBinding for the Source and find the path in code: `public string SpinnerLogoPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\images\mso_spinninglogo_blue_2.gif");`. Make sure to set the file to Build=Content and copy to output directory. – The Muffin Man Nov 03 '17 at 04:15
  • I used this approach because the WpfAnimatedGif NuGet package ddidn't work well for me - seemed to glitch when under heavy CPU load. I set the gif to Build=Resource and set the Source using a relative path from the folder the Window was in e.g. Source="../../Images/Rotating-e.gif". Worked well for me and no need for 3rd party DLLs. – Richard Moore Feb 07 '18 at 23:37
  • 2
    This is the simplest solution by far. But the problem with it is that once all the frames of the animated gif are scanned through, the animation stops. And there is no way to get the gif to animate from frame 0 again. No way to restart the animation or loop forever. At least, I haven't found a way using . – BoiseBaked Jan 25 '19 at 20:42
  • Also is unbelievably slow and full of thread racing issues between its methods. Grrr…. – BoiseBaked Jan 26 '19 at 00:06
11

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
}
kevinarpe
  • 20,319
  • 26
  • 127
  • 154
Mike Eshva
  • 1,078
  • 15
  • 17
  • 15
    This code is a part of one of my projects. I am a russian developer who works in Russia. So comments are also in Russian. Not every project in the world is an "american-english" project, Corey. – Mike Eshva Nov 15 '10 at 14:34
  • 2
    tried using your code with the following markup: but so far nothing is happening – Sonic Soul Nov 14 '11 at 12:35
  • if i change it to using a jpeg, it shows the still image. just not the gif. nice code BTW – Sonic Soul Nov 14 '11 at 12:51
  • Brilliant, I needed a solution where I could but a GIF from the Resource Dictionary -> BitmapImage -> animated GIF. THIS is it! – m.t.bennett May 16 '14 at 05:56
10

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

MHN
  • 103
  • 2
  • 7
matuuar
  • 399
  • 4
  • 9
  • 4
    This is the same (less detailed) answer as @IgorVaschuk's one from June 2012, currently the 2nd place solution votes-wise. – Heath Carroll Nov 05 '15 at 17:18
6

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.

wip
  • 2,313
  • 5
  • 31
  • 47
Ken
  • 61
  • 1
  • 1
  • 1
    I just had to set UriSource to pack://application:,,,/Images/loader.gif. Setting UriSource or Source to a relative Uri failed at runtime. – Farzan Nov 04 '13 at 19:17
  • Yes, I have tried it and I'm getting an exception. It doesn't work with relative uris. – SuperJMN Jan 28 '16 at 16:26
5

Basically the same PictureBox solution above, but this time with the code-behind to use an Embedded Resource in your project:

In XAML:

<WindowsFormsHost x:Name="_loadingHost">
  <Forms:PictureBox x:Name="_loadingPictureBox"/>
</WindowsFormsHost>

In Code-Behind:

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;
    }
}
Lynn
  • 1,103
  • 15
  • 29
  • Good addition. Really streamlines it, from what I can tell. (That said, I haven't written in WPF in over three years, now.) – CodeMouse92 Oct 24 '13 at 20:38
  • I don't really think this is a good idea because one of the main reasons you go with WPF is because of its display scaling. You'll end up with one artifact (the image) that doesn't scale properly. – The Muffin Man Nov 03 '17 at 04:17
3

I had this issue, until I discovered that in WPF4, you can simulate your own keyframe image animations. First, split your animation into a series of images, title them something like "Image1.gif", "Image2,gif", and so on. Import those images into your solution resources. I'm assuming you put them in the default resource location for images.

You are going to use the Image control. Use the following XAML code. I've removed the non-essentials.

<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>
CodeMouse92
  • 6,840
  • 14
  • 73
  • 130
  • 1
    It appears that one down-side of this approach is that by default the animation continues even after it is Collapsed, which can cause a performance hit. – Lynn Jul 16 '11 at 01:24
  • It's not DiscreteObjectKeyFrames, it is DiscreteObjectKeyFrame. Singular. – jairhumberto Feb 12 '20 at 18:50
  • @jairhumberto I think that may have changed between versions. This is quite old (2011), but I was indeed using this exact code in a project. – CodeMouse92 Feb 19 '20 at 18:07
3

An alternative to waiting animation in WPF is:

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

It will show an animated progress bar.

  • 1
    The question isn't necessarily asking about a waiting animation—it's asking about animated GIFs in general. Obviously, that _could_ be for a waiting animation, in which case this might be an appropriate alternative. But it could just as easily for any number of other media needs. – Jeremy Caney Jun 12 '20 at 20:22
  • I am showing a progressbar while databinding and rendering finishes up - but the animation of the progressbar is almost frozen, so trying to use a gif animation instead and hopning it has a higher rendering priority than the progressbar. Databinding has quite a high dispatcher priority. That lead to this SO post, suggesting to use a progressbar instead of a gif annimation. ;-) – Bent Rasmussen Nov 07 '20 at 11:10
3

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);
   }
}
sondlerd
  • 1,025
  • 1
  • 10
  • 15
  • re: **when I tried to dispose of the stream** According to the MSDN, a Bitmap that uses a Stream must have the Stream remain alive for the life of the Bitmap. The work-around is to either Freeze or Clone the bitmap. – Jesse Chisholm Dec 07 '12 at 16:20
  • 1
    He just needed to say to set `.ImageLocation` instead of `.Image`. He had the wrong method. `.ImageLocation` works off the root of the Visual Studio project, so say you have an `Images` folder, your path is then `imgBox.ImageLocation = "/Images/my.gif";`. If you have a folder called `Views` where you have a View that will show the image, to get back up to `Images`, you'd have to use 2 dots: `imgBox.ImageLocation = "../Images/my.gif";`. – vapcguy Oct 18 '16 at 19:15
1

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.

Chobits
  • 65
  • 7
  • Excellent sample. Needs Initialize updated to check for `IsAutoStart`, but otherwise, worked like a champ! – Steve Danner Feb 18 '14 at 14:37
  • 1
    Explicitly calling GC.Collect() has horrible impact on performance. – Kędrzu Jun 02 '15 at 14:57
  • This is the best answer. Worked really nicely and does not consume a gig of ram while animating like the nuget library. Just a little jittery, will see if i can fix it – saksham Jun 22 '21 at 14:43
1

I suggest using the WebBrowser control.

If the gif is on the web, you can set the source in XAML:

<WebBrowser Source="https://media.giphy.com/media/Ent2j55lyQipa/giphy.gif" />

If it is a local file you can create the Source from code-behind.

XAML:

<WebBrowser x:Name="WebBrowser" />

Code-behind:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    string curDir = Directory.GetCurrentDirectory();
    this.WebBrowser.Source = new Uri(String.Format("file:///{0}/10-monkey.gif", curDir));
}
Kip Morgan
  • 728
  • 3
  • 12
0

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

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;
    }
cmd
  • 11,622
  • 7
  • 51
  • 61
Vojta
  • 1
0

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

olasammy
  • 6,676
  • 4
  • 26
  • 32
0

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);
Heitor Castro
  • 833
  • 6
  • 9
0

Check my code, I hope this helped you :)

         public async Task GIF_Animation_Pro(string FileName,int speed,bool _Repeat)
                    {
    int ab=0;
                        var gif = GifBitmapDecoder.Create(new Uri(FileName), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
                        var getFrames = gif.Frames;
                        BitmapFrame[] frames = getFrames.ToArray();
                        await Task.Run(() =>
                        {


                            while (ab < getFrames.Count())
                            {
                                Thread.Sleep(speed);
try
{
                                Dispatcher.Invoke(() =>
                                {
                                    gifImage.Source = frames[ab];
                                });
                                if (ab == getFrames.Count - 1&&_Repeat)
                                {
                                    ab = 0;

                                }
                                ab++;
            }
 catch
{
}

                            }
                        });
                    }

or

     public async Task GIF_Animation_Pro(Stream stream, int speed,bool _Repeat)
            {
 int ab = 0;   
                var gif = GifBitmapDecoder.Create(stream , BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
                var getFrames = gif.Frames;
                BitmapFrame[] frames = getFrames.ToArray();
                await Task.Run(() =>
                {


                    while (ab < getFrames.Count())
                    {
                        Thread.Sleep(speed);
    try
    {


                     Dispatcher.Invoke(() =>
                        {
                            gifImage.Source = frames[ab];
                        });
                        if (ab == getFrames.Count - 1&&_Repeat)
                        {
                            ab = 0;

                        }
                        ab++;
    }
     catch{} 



                    }
                });
            }
0

You could try using a WebBrowser UI Control and setting the html string to an HTML image tag...

XML:

    <WebBrowser Name="webBrowser1"/>

C#: f

    webBrowser1.NavigateToString(        
      @"<img src=""arrow-24.png""/>"
    );
Bimo
  • 5,987
  • 2
  • 39
  • 61
0

i have some simple solution for animate gif images as separated frames. All u need is - to pass List of images in new class:

class AniImage: Image
    {
        private Int32Animation ani = new Int32Animation(); 
        private List<Image> images; 
        public int duration { get; set; } 
        static FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(new PropertyChangedCallback(ChangedCallbackMethod));
        public static readonly DependencyProperty frameProperty = DependencyProperty.Register("Frame", typeof(int), typeof(AniImage), metadata);
        static void ChangedCallbackMethod(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {            
            AniImage ai = (AniImage)d; 
            int curFrame = (int)e.NewValue; 
            ai.Source = ai.images[curFrame].Source; 
        }            
        public int Frame
        {
            get { return (int)GetValue(frameProperty); }
            set { SetValue(frameProperty, value); }
        }
        public AniImage(List<Image> imgs)
        {
            images = imgs; 
            duration = 2; 
            Frame = 0; 
            this.Source = imgs[0].Source;                
            if(images.Count > 1)
            {
                ani.From = 0;
                ani.To = images.Count - 1;
                ani.By = 1;
                ani.Duration = TimeSpan.FromSeconds(duration);
                ani.RepeatBehavior = RepeatBehavior.Forever;
                BeginAnimation(frameProperty, ani); 
            }           
        }
    }

For create new object:

        List<Image> imagesList = new List<Image>();
        string[] imgNames = {
            "Resources/spaceship021.gif",
            "Resources/spaceship022.gif",
            "Resources/spaceship023.gif",
            "Resources/spaceship024.gif",
            "Resources/spaceship025.gif" };

        foreach (string s in imgNames)
        {
            Image img = new Image();
            img.Source = new BitmapImage(new Uri(s, UriKind.Relative));
            imagesList.Add(img);
        }

        ship = new AniImage(imagesList);

        ship.Width = 100;
        ship.Height = 100;
        Canvas.SetLeft(ship, 250);
        Canvas.SetBottom(ship, 50);
        field.Children.Add(ship);
Klo
  • 23
  • 6