WPF'de çalışmak için animasyonlu bir gif'i nasıl edinebilirim?


218

Ne kontrol tipi kullanmalısınız - Image, MediaElementvs.?


4
Aşağıda, aşağıdaki çözümlerin kısa bir özeti verilmiştir. Bunları VS2015 kullanarak uyguladım. Dario tarafından gönderilen GifImage sınıfı harika çalıştı, ancak bazı giflerim sunuldu. Pradip Daunde ve nicael'in MediaElement yaklaşımı önizleme alanında çalışıyor gibi gözüküyor, ancak giflerimin hiçbiri çalışma sırasında görüntülenmedi. IgorVaschuk ve SaiyanGirl'in WpfAnimatedGif çözümü sorunsuz bir şekilde çalıştı, ancak bir üçüncü taraf kütüphanesi kurmayı gerektirdi (açıkçası). Gerisini ben denemedim.
Heath Carroll

Yanıtlar:


214

Bu sorunun en popüler yanıtını (yukarıda Dario tarafından) düzgün çalışmak için alamadım. Sonuç, garip eserler ile garip, dalgalı animasyon oldu. Şimdiye kadar bulduğum en iyi çözüm: https://github.com/XamlAnimatedGif/WpfAnimatedGif

NuGet ile kurabilirsiniz

PM> Install-Package WpfAnimatedGif

ve gif görüntüsünü eklemek ve aşağıdaki gibi kullanmak istediğiniz Pencereye yeni bir ad alanında kullanmak için

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

Paket gerçekten temiz, aşağıdaki gibi bazı özellikleri ayarlayabilirsiniz

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

ve kodunuzda da kullanabilirsiniz:

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

EDIT: Silverlight desteği

Josh2112'nin yorumuna göre, Silverlight projenize animasyonlu GIF desteği eklemek istiyorsanız github.com/XamlAnimatedGif/XamlAnimatedGif kullanın


13
Bu harika çalıştı ve uygulanması 60 saniyeden az sürdü. Teşekkürler!
Ryan Sorensen

3
Özellikle C # kullanarak size güvenmediği için popüler olanlardan herhangi biri IMO'dan çok daha iyi cevap
Jamie E

8
Bu, kabul edilen cevaptan çok daha iyidir: gif'in meta verilerini kullanır, dalgalı değil, bir NuGet paketidir, dil agnostiktir. Ben stackoverflow kabul edilen cevaba güven bir oy için izin isterdim.
John Gietzen

6
Kamu hizmeti duyurusu: WpfAnimatedGif'in yazarı projesini XamlAnimatedGif olarak 'yeniden başlattı' ve WPF, Windows Mağazası (Win8), Windows 10 ve Silverlight'ı destekliyor
josh2112

2
imgBurada ne var?
amit jha

104

Görüntü kontrolünü genişleten ve Gif Decoder'ı kullanarak bir çözüm yayınlıyorum. GIF kod çözücüsü bir çerçeve özelliğine sahiptir. FrameIndexMülkü canlandırıyorum . Olay ChangingFrameIndex, source özelliğini FrameIndex(kod çözücüde bulunan) öğesine karşılık gelen kareye değiştirir . GIF'in saniyede 10 kare olduğunu tahmin ediyorum.

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

Kullanım örneği (XAML):

<controls:GifImage x:Name="gifImage" Stretch="None" GifSource="/SomeImage.gif" AutoStart="True" />

1
Ek referanslara ihtiyacınız olmadığından, bu XBAP uygulamaları için daha iyi çalışır.
Max Galkin

1
Çok havalı. Yapıcı kodunuzu "Başlatıldı" olayına koyarak ve bir Uri özelliği ekleyerek, bu denetim bir XAML dosyasına da yerleştirilebilir.
flq

1
+1, iyi olan! Ancak, görüntünün gerçek kare süresini dikkate almaz ... Bu bilgileri okumak için bir yol bulabilirseniz, birInt32AnimationUsingKeyFrames
Thomas Levesque

7
Aslında, framerate GIF için sabittir, bu yüzden sonuçta anahtar karelere ihtiyacınız yoktur ... gf.Frames[0].MetaData.GetQuery("/grctlext/Delay")Çerçeveyi okuyabilirsiniz (yüzlerce saniye içinde kare süresi olan bir ushort döndürür)
Thomas Levesque

3
@vidstige, evet, neden bu yorumu (neredeyse 2 yıl önce) yaptığımı hatırlamıyorum. Gecikmenin her kare için farklı olabileceğinin farkındayım ve WPF Animasyonlu GIF kütüphanem bunu doğru bir şekilde dikkate alıyor.
Thomas Levesque

38

Ben de bir arama yaptım ve eski MSDN forumlarındaki bir iş parçacığında birkaç farklı çözüm buldum. (bağlantı artık çalışmadı, bu yüzden kaldırdım)

Yürütmenin en basit yolu bir WinForms PictureBoxdenetimi kullanmak gibi görünüyor ve böyle gitti (iş parçacığından birkaç şeyi değiştirdi, çoğu aynı).

Bir başvuru ekleyin System.Windows.Forms, WindowsFormsIntegrationve System.Drawingilk projenize.

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

Daha sonra Window_Loadedişleyicide pictureBoxLoading.ImageLocationözelliği göstermek istediğiniz görüntü dosyası yoluna ayarlarsınız.

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

MediaElementKontrol bu iş parçacığı bahsedildi, ama dayalı en az 2 homebrewed kontrolleri de dahil olmak üzere, birtakım seçenekler vardı yüzden de, bu oldukça ağır kontrolü olduğu belirtilen Imagedenetim bu en basit yani.


WindowsFormsHost kullanırken bu ana pencereyi AllowTransparency = "True" ile koyabilir misiniz?
Junior Mayhé

@Junior: Evet, ayarlayabilirsiniz AllowTransparency="True". Bunun aklınızda bulunan sonuçları üretip üretmeyeceği başka bir konudur. Kendim denemedim, ama bunun WindowsFormsHostşeffaf olmayacağına bahse girerim . Gücün geri kalanı Window. Sadece denemen gerekecek diye düşünüyorum.
Joel B Fant

Winform API nedeniyle pictureBoxLoading.Image ile sorun vardı. Sorunumu çözen kodun altına gönderdim. Çözümünüz için teşekkürler Joel!
sondlerd

Senin gibi öldü. Bu konu muydu ?
çalışması devam ediyor

2
Entegrasyon referansı eklerken, arayüzümdeki
yu yang Jian

36

Bu küçük uygulama hakkında: Arkasındaki kod:

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>

1
Güzel ! Kısa kod, işi iyi yapıyor. Daha fazla oyu olmadığına inanamıyorum.
çalışması devam ediyor

2
En iyi cevap ... Üstte olmalı! Ben arkasında herhangi bir kod olmadan çalışmasını başardı - sadece bu <MediaElement LoadedBehavior="Play" Source="{Binding MyGifFile}" >- MyGifFile benim animasyonlu gif sadece dosya adı (ve yolu).
Anthony Nichols

Tanrım, neden bağlanmak ListBoxya da bağlanmak zahmetine giresin ki ? Bağlamadan denedim, sadece Dosya yolunu Kaynak'a koyun ve görünür, ancak animasyon oluşturmaz. Bağlamayı kullanırsam, benim için ListBoxhiç gelmez, benim için - göründüğümde kullandığımla aynı olsa bile, dosya yolumun yanlış olduğu konusunda bana bir istisna verecektir.
vapcguy

Güncellenmesi uzun sürüyor ve her görüntülendiğinde güncellenmesi gerekiyor.
Yola

15

Kullanmanız çok basit <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" />

Her ihtimale karşı dosyanız app Kaynağa için DataBinding kullanmak ve kodunda yolunu bulabilirsiniz içine paketlenir: public string SpinnerLogoPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\images\mso_spinninglogo_blue_2.gif");. Dosyayı Build = Content olarak ayarladığınızdan ve çıktı dizinine kopyaladığınızdan emin olun.
Muffin Man

Bu yaklaşımı kullandım çünkü WpfAnimatedGif NuGet paketi benim için iyi çalışmıyor - ağır CPU yükü altında aksaklık gibi görünüyordu. Gif'i Build = Resource olarak ayarladım ve Source'u, Window'un bulunduğu kaynaktan göreceli bir yol kullanarak ayarladım Örn. Benim için iyi çalıştı ve üçüncü taraf DLL gerek yok.
Richard Moore

Bu şimdiye kadarki en basit çözüm. Ancak sorun, animasyonlu gif'in tüm kareleri tarandıktan sonra animasyonun durmasıdır. Ve gif'in tekrar 0 karesinden animasyon almasını sağlamanın bir yolu yoktur. Animasyonu veya döngüyü sonsuza kadar yeniden başlatmanın bir yolu yoktur. En azından <MediaElement /> kullanmanın bir yolunu bulamadım.
BoiseBaked

Ayrıca <MediaElement /> inanılmaz derecede yavaştır ve yöntemleri arasında iplik yarışları ile doludur. Grrr ....
BoiseBaked

10

İşte benim animasyonlu görüntü kontrol versiyonum. Görüntü kaynağını belirtmek için Kaynak standart özelliğini kullanabilirsiniz. Daha da geliştirdim. Ben bir rusyım, proje rusça bu yüzden yorumlar da Rusça. Ama yine de her şeyi yorum yapmadan anlayabilmelisin. :)

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

15
Bu kod projelerimden birinin bir parçası. Rusya'da çalışan bir Rus geliştiriciyim. Yani yorumlar da Rusça. Dünyadaki her proje bir "amerikan-ingilizce" projesi değil, Corey.
Mike Eshva

2
kodunuzu aşağıdaki biçimlendirme ile kullanmayı denediniz: <local: AnimatedImage Source = "/ Resources / ajax-loader.gif" /> ancak şu ana kadar hiçbir şey olmuyor
Sonic Soul

Eğer bir jpeg kullanarak değiştirirseniz, hareketsiz görüntüyü gösterir. gif değil. güzel kod BTW
Sonic Soul

Zekice, Kaynak Sözlük -> BitmapImage -> animasyonlu GIF'den bir GIF alabileceğim bir çözüme ihtiyacım vardı. Budur!
mtbennett

9

Bu kütüphaneyi kullanıyorum: https://github.com/XamlAnimatedGif/WpfAnimatedGif

İlk olarak, kitaplığınızı projenize yükleyin (Paket Yöneticisi Konsolu'nu kullanarak):

    PM > Install-Package WpfAnimatedGif

Ardından, bu snippet'i XAML dosyasına kullanın:

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

Umarım yardımcı olur.

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


3
Bu, @ IgorVaschuk'un Haziran 2012'deki yanıtıyla aynı (daha az ayrıntılı) cevaptır, şu anda oy pusulasında ikinci sırada yer almaktadır.
Heath Carroll

5

Temelde yukarıdaki aynı PictureBox çözümü, ancak bu sefer projenizde Gömülü Kaynak kullanmak için arka plan koduyla:

XAML'de:

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

Arkasında Kod:

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

İyi bir ek. Gerçekten anlatabilirim. (Yani, şimdi üç yıldan fazla bir süredir
WPF'de yazmadım

Bunun iyi bir fikir olduğunu düşünmüyorum çünkü WPF ile gitmenizin ana nedenlerinden biri ekran ölçeklemesi. Sonunda düzgün ölçeklenmeyen bir eser (resim) elde edersiniz.
Muffin Man

5

Mike Eshva'nın kodunu değiştirdim ve daha iyi çalışmasını sağladım. 1frame jpg png bmp veya mutil-frame gif ile kullanabilirsiniz. BitmapImage olan Kaynak özelliğini bağladığınız bellek akışı.

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

Bu özel bir kontroldür. WPF App Project'te oluşturmanız ve Şablon geçersiz kılma stilini silmeniz gerekir.


1
Sadece UriSource'u paketlemek zorunda kaldım: // application: ,,, / Images / loader.gif. UriSource veya Source'ı göreceli bir Uri'ye ayarlamak çalışma zamanında başarısız oldu.
Farzan

Evet, denedim ve bir istisna alıyorum. Nispi ürelerde çalışmaz.
SuperJMN

3

WPF4'te kendi ana kare görüntü animasyonlarınızı simüle edebileceğinizi öğrenene kadar bu sorunu yaşadım. İlk olarak, animasyonunuzu bir dizi resme bölün, onlara "Image1.gif", "Image2, gif" gibi bir şey verin. Bu görüntüleri çözüm kaynaklarınıza aktarın. Bunları resimler için varsayılan kaynak konumuna koyduğunuzu varsayıyorum.

Görüntü kontrolünü kullanacaksınız. Aşağıdaki XAML kodunu kullanın. Gerekli olmayanları kaldırdım.

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

1
Bu yaklaşımın aşağı taraflarından biri, varsayılan olarak animasyonun Daraltıldıktan sonra bile devam etmesidir ve bu da performansa neden olabilir.
Lynn

DiscreteObjectKeyFrames değil, DiscreteObjectKeyFrame. Tekil.
jairhumberto

@jairhumberto Bence bu sürümler arasında değişmiş olabilir. Bu oldukça eski (2011), ama gerçekten bir projede bu tam kodu kullanıyordum.
CodeMouse92

3

Gönderiminiz için teşekkürler Joel, WPF'nin animasyonlu GIF desteği olmadığını çözmeme yardımcı oldu. Sadece Wincodes api nedeniyle pictureBoxLoading.Image özelliği ayarlama ile bir saat halkı vardı küçük bir kod ekleyerek.

Animasyonlu gif resmimin Build Action'ını "İçerik" ve Çıktıya kopyala dizinini "Daha yeniyse kopyala" veya "her zaman" olarak ayarlamak zorunda kaldım. Sonra MainWindow () bu yöntemi çağırdı. Tek sorun, akışı atmaya çalıştığımda, görüntüm yerine kırmızı bir zarf grafiği verdi. Bu sorunu çözmem gerekecek. Bu, bir BitmapImage yükleme ve bir Bitmap'e değiştirme ağrısını kaldırdı (ki bu artık bir gif olmadığı için animasyonumu öldürdü).

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

re: akışı atmaya çalıştığımda MSDN'ye göre, bir Akış kullanan bir Bitmap , Akışın Bitmap'in ömrü boyunca canlı kalmasını sağlamalıdır. Çözüm, bitmap'i dondurmak veya klonlamaktır.
Jesse Chisholm

1
Sadece .ImageLocationyerine koymak için söylemeliydi .Image. Yanlış bir yöntemi vardı. .ImageLocationVisual Studio projesinin kökü çalışır, bu nedenle bir Imagesklasörünüz olduğunu varsayalım, yol o zaman imgBox.ImageLocation = "/Images/my.gif";. Adlı bir klasör varsa Viewsayağa geri almak için, görüntüyü gösterecek bir görünümü var, nerede Images, size 2 nokta kullanıyor olurdu: imgBox.ImageLocation = "../Images/my.gif";.
vapcguy

1

Yukarıdaki her şeyi denedim, ama her birinin kısalığı var ve hepinize teşekkürler, kendi GifImage'ımı çalışıyorum:

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

Kullanımı:

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

Bellek sızıntısına neden olmayacağı ve gif görüntüsünün kendi zaman çizgisini canlandırdığı için deneyebilirsiniz.


Mükemmel örnek. Needs Initialize, kontrol etmek için güncellendi IsAutoStart, ancak aksi halde bir şampiyon gibi çalıştı!
Steve Danner

1
GC.Collect () işlevinin açıkça çağrılması performans üzerinde korkunç bir etkiye sahiptir.
Kędrzu

0

Daha önce benzer bir sorunla karşılaştım .gif, projenizde dosya oynamam gerekiyordu . İki seçeneğim vardı:

  • WinForms'dan PictureBox kullanma

  • codeplex.com adresinden WPFAnimatedGif gibi bir üçüncü taraf kitaplığı kullanma .

Sürümü PictureBoxbenim için çalışmadı ve proje bunun için harici kitaplıklar kullanamadı. Ben Bitmapde yardımla kendim için yaptım ImageAnimator. Çünkü standart BitmapImage, oynatmayı desteklemiyor.gif dosyaların .

Tam örnek:

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

BitmapURI yönergesini desteklemediğinden .gif, geçerli dizinden dosya yüklüyorum .


0

GifImage.Initialize()GIF meta verilerinden uygun çerçeve zamanlamasını okuyan yöntemin küçük iyileştirilmesi .

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

0

Bu çözülmüş olup olmadığını emin değilim ama en iyi yolu WpfAnimatedGid kitaplığı kullanmaktır . Kullanımı çok kolay, basit ve basittir. Sadece 2 satır XAML kodu ve yaklaşık 5 satır C # Kodu gerektirir.

Bunun nasıl kullanılabileceğine dair gerekli tüm ayrıntıları göreceksiniz. Bu, tekerleği yeniden icat etmek yerine kullandığım şeydi


0

WpfAnimatedGif'in kullanılmasını öneren ana yanıta ek olarak , animasyonun gerçekten çalıştığından emin olmak için bir görüntüyü Gif ile değiştiriyorsanız , sonuna aşağıdaki satırları eklemeniz gerekir :

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

Yani kodunuz şöyle görünecek:

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

Kodumu kontrol et, umarım bu sana yardımcı olmuştur :)

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

                            }
                        });
                    }

veya

     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

WPF'de bekleyen animasyona bir alternatif:

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

Animasyonlu bir ilerleme çubuğu gösterecektir.


1
Soru mutlaka bekleyen bir animasyon sormuyor - genel olarak animasyonlu GIF'leri soruyor. Açıkçası, bu bir bekleme animasyonu için olabilir , bu durumda bu uygun bir alternatif olabilir. Ancak diğer birçok medya ihtiyacı için bu kadar kolay olabilir.
Jeremy Caney
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.