Search code examples
c#wpfxamlbindinganimated-gif

How do I get this WPF .gif to play on update?


Question:

  • Why does my .gif load, but not play when updating using GifSourcePropertyChanged?

About .gif loading:
Currently I have use U.IsValidURL() to keep new Uri from throwing an Exception. This is because I need GifImage to load with the application and then update later from ViewModel. Everything goes fine until GifSourcePropertyChanged is called, then local:GifImage is updated, but the .gif doesn't play (I believe it loads as MediaElement loads in parallel and local:GifImage turns black where the .gif is suppose to load). If I hard code a sample Uri from the beginning, local:GifImage loads and plays just fine.

Example XAML:

<local:GifImage 
        GifSource="{    
            Binding Path=myGifImage, 
            UpdateSourceTrigger=PropertyChanged, 
            Mode=OneWay}"
        AutoStart="True"/>

Example ViewModel:

public class ViewModel: INotifyPropertyChanged
{                   
    private string _myGifImage; 

    public string myGifImage
    {
        get { return this._myGifImage; }
        set
        {
            this._myGifImage = value;
            this.OnPropertyChanged("myGifImage");
        }
    }
}

Example GifImage Class:

public class GifImage : Image
{
    #region Memmbers

    private GifBitmapDecoder _gifDecoder;
    private Int32Animation _animation;
    private bool _isInitialized;

    #endregion Memmbers

    #region Properties

    private int FrameIndex
    {
        get { return (int)GetValue(FrameIndexProperty); }
        set { SetValue(FrameIndexProperty, value); }
    }

    private static readonly DependencyProperty FrameIndexProperty =
     DependencyProperty.Register("FrameIndex", typeof(int), typeof(GifImage), new FrameworkPropertyMetadata(0, new PropertyChangedCallback(ChangingFrameIndex)));

    private static void ChangingFrameIndex(DependencyObject obj, DependencyPropertyChangedEventArgs ev)
    {
        GifImage image = obj as GifImage;
        image.Source = image._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)
    {
        // CARLO 20100622: Reinitialize animation everytime image is changed
        (sender as GifImage).Initialize();
    }

    #endregion Properties

    #region Private Instance Methods

    private void Initialize()
    {

        if (U.IsValidURL(this.GifSource, UriKind.Absolute))
        {
            _gifDecoder = new GifBitmapDecoder(
                new Uri(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;
        }

    }

    #endregion Private Instance Methods

    #region Public Instance Methods

    /// <summary>
    /// Shows and starts the gif animation
    /// </summary>
    public void Show()
    {
        this.Visibility = Visibility.Visible;
        this.StartAnimation();
    }

    /// <summary>
    /// Hides and stops the gif animation
    /// </summary>
    public void Hide()
    {
        this.Visibility = Visibility.Collapsed;
        this.StopAnimation();
    }

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

    #endregion Public Instance Methods
}  

I got GifImage from somewhere on SO (I can't remember which question, there are several on WPF and .gifs). GifImage was the only class that didn't have a ton of errors (at least for me).


Solution

  • Not sure what the problem is with your code, but you could try to use this attached property instead:

    <Image my:ImageBehavior.AnimatedSource="{Binding Path=myGifImage}"/>