Bagaimana cara mendapatkan gif animasi untuk bekerja di WPF?

218

Apa jenis kontrol yang harus saya gunakan - Image, MediaElement, dll?

cakar besi
sumber
4
Berikut ini ringkasan terbaru dari solusi di bawah ini. Saya menerapkan ini menggunakan VS2015. Kelas GifImage yang dikirimkan oleh Dario bekerja dengan sangat baik, tetapi beberapa gif saya dibuat artifak. Pendekatan MediaElement oleh Pradip Daunde dan nicael tampaknya berfungsi di area pratinjau, tetapi tidak ada gif yang ditampilkan saat runtime. Solusi WpfAnimatedGif oleh IgorVaschuk dan SaiyanGirl bekerja sangat baik tanpa masalah tetapi harus menginstal perpustakaan pihak ketiga (jelas). Saya tidak mencoba sisanya.
Heath Carroll

Jawaban:

214

Saya tidak bisa mendapatkan jawaban paling populer untuk pertanyaan ini (di atas oleh Dario) agar berfungsi dengan baik. Hasilnya aneh, animasi berombak dengan artefak aneh. Solusi terbaik yang saya temukan sejauh ini: https://github.com/XamlAnimatedGif/WpfAnimatedGif

Anda dapat menginstalnya dengan NuGet

PM> Install-Package WpfAnimatedGif

dan untuk menggunakannya, di namespace baru ke Window di mana Anda ingin menambahkan gambar gif dan menggunakannya seperti di bawah ini

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

Paketnya sangat rapi, Anda bisa mengatur beberapa atribut seperti di bawah ini

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

dan Anda dapat menggunakannya dalam kode Anda juga:

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

EDIT: Dukungan Silverlight

Sesuai komentar josh2112 jika Anda ingin menambahkan dukungan GIF animasi ke proyek Silverlight Anda, gunakan github.com/XamlAnimatedGif/XamlAnimatedGif

Igor Vaschuk
sumber
13
Ini bekerja dengan baik, dan membutuhkan waktu kurang dari 60 detik untuk diimplementasikan. Terima kasih!
Ryan Sorensen
3
Jawaban yang jauh lebih baik daripada IMO yang populer, terutama karena itu tidak bergantung pada Anda menggunakan C #
Jamie E
8
Ini jauh lebih baik daripada jawaban yang diterima: menggunakan metadata gif, bukan berombak, adalah paket NuGet, adalah agnostik bahasa. Saya berharap stackoverflow diizinkan untuk pemungutan suara yang tidak percaya pada jawaban yang diterima.
John Gietzen
6
Pengumuman layanan publik: Penulis WpfAnimatedGif telah 'reboot' proyeknya sebagai XamlAnimatedGif, dan mendukung WPF, Windows Store (Win8), Windows 10, dan Silverlight: github.com/XamlAnimatedGif/XamlAnimatedGif
josh2112
2
Ada apa imgdisini
amit jha
104

Saya memposting solusi memperluas kontrol gambar dan menggunakan Gif Decoder. Decoder gif memiliki properti frames. Saya menghidupkan FrameIndexproperti. Acara ChangingFrameIndexmengubah properti sumber ke bingkai yang sesuai dengan FrameIndex(yang ada di dekoder). Saya kira gif memiliki 10 frame per detik.

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

Contoh penggunaan (XAML):

<controls:GifImage x:Name="gifImage" Stretch="None" GifSource="/SomeImage.gif" AutoStart="True" />
Marius Bancila
sumber
1
Yang ini berfungsi, dan lebih baik untuk aplikasi XBAP, karena Anda tidak memerlukan referensi tambahan.
Max Galkin
1
Itu keren. Dengan meletakkan kode konstruktor Anda di acara "Inisialisasi" dan memperkenalkan properti Uri, kontrol ini juga dapat ditempatkan dalam file XAML.
flq
1
+1, bagus! Namun, itu tidak memperhitungkan durasi bingkai aktual gambar ... Jika Anda dapat menemukan cara untuk membaca informasi itu, Anda dapat mengubah kode untuk menggunakanInt32AnimationUsingKeyFrames
Thomas Levesque
7
Sebenarnya, framerate adalah konstan untuk GIF, jadi Anda tidak perlu keyframe setelah semua ... Anda dapat membaca framerate dengan gf.Frames[0].MetaData.GetQuery("/grctlext/Delay")(mengembalikan ushort yang merupakan durasi bingkai dalam ratusan detik)
Thomas Levesque
3
@vidstige, ya, saya tidak ingat mengapa saya membuat komentar ini pada saat itu (hampir 2 tahun yang lalu). Saya menyadari bahwa keterlambatan dapat berbeda untuk setiap bingkai, dan pustaka GIF Animasi WPF saya memperhitungkannya dengan benar.
Thomas Levesque
38

Saya juga melakukan pencarian dan menemukan beberapa solusi berbeda hanya dalam utas di forum MSDN lama. (tautan tidak lagi berfungsi jadi saya menghapusnya)

Yang paling sederhana untuk dieksekusi tampaknya menggunakan PictureBoxkontrol WinForms , dan berjalan seperti ini (mengubah beberapa hal dari utas, sebagian besar sama).

Menambahkan referensi ke System.Windows.Forms, WindowsFormsIntegrationdan System.Drawinguntuk proyek Anda pertama.

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

Kemudian di Window_Loadedhandler, Anda akan mengatur pictureBoxLoading.ImageLocationproperti ke jalur file gambar yang ingin Anda tampilkan.

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

The MediaElementkontrol disebutkan di thread itu, tetapi juga disebutkan bahwa itu adalah kontrol agak berat, jadi ada beberapa alternatif, termasuk setidaknya 2 kontrol yg dibuat berdasarkan pada Imagekontrol, jadi ini adalah yang paling sederhana.

Joel B Fant
sumber
dapatkah Anda meletakkan jendela utama ini dengan AllowTransparency = "True" saat menggunakan WindowsFormsHost?
Junior Mayhé
@ Junior: Ya, Anda bisa mengatur AllowTransparency="True". Apakah itu akan menghasilkan hasil yang Anda pikirkan atau tidak adalah masalah lain. Saya sendiri belum mencobanya, tetapi saya berani bertaruh bahwa WindowsFormsHostitu tidak akan transparan sama sekali. Sisa Windowkekuatan. Anda hanya harus mencobanya, saya pikir.
Joel B Fant
Saya mengalami masalah dengan pictureBoxLoading.Image karena winform API. Saya memposting kode di bawah ini yang menyelesaikan masalah saya. Terima kasih atas solusinya, Joel!
sondlerd
Sepertinya orangmu sudah mati. Apakah ini utas ini ?
usap
2
Saat menambahkan referensi Integrasi, namanya di UI saya adalah WindowsFormsIntegration, tanpa titik: i.imgur.com/efMiC23.png
yu yang Jian
36

Bagaimana dengan aplikasi mungil ini: Kode di belakang:

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
sumber
1
Bagus! Kode pendek, melakukan pekerjaan dengan baik. Saya tidak percaya itu tidak memiliki lebih banyak upvotes.
hapus
2
Jawaban terbaik ... Harus di atas! Saya dapat membuatnya berfungsi tanpa kode di belakang - hanya ini <MediaElement LoadedBehavior="Play" Source="{Binding MyGifFile}" >- MyGifFile hanya nama file (dan jalur) dari gif animasi saya.
Anthony Nichols
Astaga, mengapa repot-repot untuk mengikat ListBox, atau mengikat sama sekali? Saya mencobanya tanpa mengikat, cukup letakkan file path di Source dan muncul, tetapi tidak menghidupkan. Jika saya menggunakan penjilidan, bahkan dengan ListBox, itu tidak muncul sama sekali, bagi saya - itu akan memberi saya pengecualian bahwa jalur file saya salah, meskipun itu sama dengan yang saya gunakan ketika muncul.
vapcguy
Diperlukan waktu lama untuk memperbarui dan perlu diperbarui setiap kali mulai terlihat.
Yola
15

Sangat sederhana jika Anda menggunakan <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" />
Pradip Daunde
sumber
Hanya dalam kasus file Anda dikemas dalam aplikasi Anda, Anda dapat menggunakan penyatuan data untuk Sumber dan menemukan jalan dalam kode: public string SpinnerLogoPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\images\mso_spinninglogo_blue_2.gif");. Pastikan untuk mengatur file ke Build = Konten dan salin ke direktori output.
The Muffin Man
Saya menggunakan pendekatan ini karena paket WpfAnimatedGif NuGet tidak berfungsi dengan baik untuk saya - sepertinya bermasalah ketika berada di bawah beban CPU yang berat. Saya mengatur gif ke Build = Resource dan mengatur Sumber menggunakan jalur relatif dari folder Window misalnya Sumber = "../../ Gambar / Memutar-e.gif". Bekerja dengan baik untuk saya dan tidak perlu untuk DLL pihak ke-3.
Richard Moore
Ini adalah solusi paling sederhana sejauh ini. Tetapi masalahnya adalah bahwa begitu semua frame dari animasi gif dipindai, animasi berhenti. Dan tidak ada cara untuk mendapatkan gif untuk menghidupkan kembali dari frame 0 lagi. Tidak ada cara untuk memulai kembali animasi atau loop selamanya. Setidaknya, saya belum menemukan cara menggunakan <MediaElement />.
BoiseBaked
<MediaElement /> juga sangat lambat dan penuh masalah balap benang di antara metodenya. Grrr ....
BoiseBaked
10

Ini adalah versi saya dari kontrol gambar animasi. Anda dapat menggunakan Sumber properti standar untuk menentukan sumber gambar. Saya semakin memperbaikinya. Saya seorang Rusia, proyek adalah bahasa Rusia jadi komentar juga dalam bahasa Rusia. Tapi bagaimanapun Anda harus bisa memahami semuanya tanpa komentar. :)

/// <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
}
Mike Eshva
sumber
15
Kode ini adalah bagian dari salah satu proyek saya. Saya adalah pengembang Rusia yang bekerja di Rusia. Jadi komentar juga dalam bahasa Rusia. Tidak semua proyek di dunia adalah proyek "american-english", Corey.
Mike Eshva
2
mencoba menggunakan kode Anda dengan markup berikut: <local: AnimatedImage Source = "/ Resources / ajax-loader.gif" /> tetapi sejauh ini tidak ada yang terjadi
Sonic Soul
jika saya mengubahnya menggunakan jpeg, itu menunjukkan gambar diam. tidak gif. kode bagus BTW
Sonic Soul
Cemerlang, saya membutuhkan solusi di mana saya bisa tetapi GIF dari Resource Dictionary -> BitmapImage -> GIF animasi. Ini dia!
mtbennett
9

Saya menggunakan perpustakaan ini: https://github.com/XamlAnimatedGif/WpfAnimatedGif

Pertama, instal perpustakaan ke proyek Anda (menggunakan Package Manager Console):

    PM > Install-Package WpfAnimatedGif

Lalu, gunakan potongan ini ke file XAML:

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

Semoga membantu.

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

matuuar
sumber
3
Ini adalah jawaban yang sama (kurang terperinci) dengan jawaban @ IgorVaschuk mulai Juni 2012, saat ini solusi ke-2 untuk solusi suara.
Heath Carroll
5

Pada dasarnya solusi PictureBox yang sama di atas, tetapi kali ini dengan kode-belakang untuk menggunakan Sumber Daya Tertanam dalam proyek Anda:

Dalam XAML:

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

Di Balik Kode:

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
sumber
Tambahan yang bagus Benar-benar merampingkannya, dari apa yang bisa saya katakan. (Yang mengatakan, saya belum menulis dalam WPF selama lebih dari tiga tahun, sekarang.)
CodeMouse92
Saya tidak berpikir ini adalah ide yang bagus karena salah satu alasan utama Anda menggunakan WPF adalah karena penskalaan tampilan. Anda akan berakhir dengan satu artefak (gambar) yang tidak skala dengan benar.
The Muffin Man
5

Saya memodifikasi kode Mike Eshva, Dan saya membuatnya bekerja lebih baik. Anda dapat menggunakannya dengan 1frame jpg png bmp atau mutil-frame gif. Jika Anda ingin mengikat uri ke kontrol, ikat properti UriSource atau Anda ingin ikat di aliran memori yang Anda ikat propertie Sumber yang merupakan 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
}

Ini adalah kontrol khusus. Anda perlu membuatnya di WPF App Project, dan menghapus Template override dengan gaya.

Ken
sumber
1
Saya hanya perlu mengatur UriSource untuk mengemas: // aplikasi: ,,, / Images / loader.gif. Mengatur UriSource atau Sumber ke Uri relatif gagal saat runtime.
Farzan
Ya, saya sudah mencobanya dan saya mendapat pengecualian. Itu tidak bekerja dengan uris relatif.
SuperJMN
3

Saya memiliki masalah ini, sampai saya menemukan bahwa di WPF4, Anda dapat mensimulasikan animasi gambar keyframe Anda sendiri. Pertama, pisahkan animasi Anda menjadi serangkaian gambar, beri judul sesuatu seperti "Image1.gif", "Image2, gif", dan sebagainya. Impor gambar-gambar itu ke sumber daya solusi Anda. Saya berasumsi Anda menempatkannya di lokasi sumber daya default untuk gambar.

Anda akan menggunakan kontrol Gambar. Gunakan kode XAML berikut. Saya telah menghapus yang tidak penting.

<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
sumber
1
Tampaknya salah satu sisi buruk dari pendekatan ini adalah bahwa secara default animasi berlanjut bahkan setelah Collapsed, yang dapat menyebabkan hit kinerja.
Lynn
Ini bukan DiscreteObjectKeyFrames, itu DiscreteObjectKeyFrame. Tunggal.
Jairhumberto
@jairhumberto Saya pikir itu mungkin telah berubah antara versi. Ini cukup lama (2011), tetapi saya memang menggunakan kode persis ini dalam suatu proyek.
CodeMouse92
3

Terima kasih atas posting Anda, Joel, ini membantu saya mengatasi tidak adanya dukungan WPF untuk GIF animasi. Hanya menambahkan sedikit kode sejak saya bersenang-senang dengan menyetel properti pictureBoxLoading.Image karena api Winforms.

Saya harus mengatur animasi gif gambar Build Action saya sebagai "Konten" dan direktori Salin ke keluaran ke "Salin jika lebih baru" atau "selalu". Kemudian di MainWindow () saya memanggil metode ini. Satu-satunya masalah adalah ketika saya mencoba untuk membuang aliran, itu memberi saya grafik amplop merah, bukan gambar saya. Saya harus menyelesaikan masalah itu. Ini menghilangkan rasa sakit saat memuat BitmapImage dan mengubahnya menjadi Bitmap (yang jelas membunuh animasi saya karena tidak lagi menjadi 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
sumber
re: ketika saya mencoba untuk membuang stream Menurut MSDN, Bitmap yang menggunakan Stream harus memiliki Stream tetap hidup untuk kehidupan Bitmap. Cara mengatasinya adalah Membekukan atau Mengkloning bitmap.
Jesse Chisholm
1
Dia hanya perlu mengatakan untuk mengatur .ImageLocationbukan .Image. Dia memiliki metode yang salah. .ImageLocationbekerja dari root proyek Visual Studio, jadi katakan Anda memiliki Imagesfolder, jalur Anda kemudian imgBox.ImageLocation = "/Images/my.gif";. Jika Anda memiliki folder bernama Viewsmana Anda memiliki View yang akan menampilkan gambar, untuk mendapatkan kembali ke Images, Anda harus menggunakan 2 titik: imgBox.ImageLocation = "../Images/my.gif";.
vapcguy
1

Saya telah mencoba semua cara di atas, tetapi masing-masing memiliki kekurangan, dan terima kasih kepada Anda semua, saya mengerjakan GifImage saya sendiri:

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

Pemakaian:

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

Karena tidak akan menyebabkan memori bocor dan membuat garis waktu gambar gif menjadi animasi, Anda dapat mencobanya.

Chobits
sumber
Sampel luar biasa. Membutuhkan Inisialisasi diperbarui untuk memeriksa IsAutoStart, tetapi jika tidak, bekerja seperti juara!
Steve Danner
1
Secara eksplisit menelepon GC.Collect () berdampak buruk pada kinerja.
Kędrzu
0

Sebelumnya, saya menghadapi masalah yang sama, saya perlu memutar .giffile di proyek Anda. Saya punya dua pilihan:

  • menggunakan PictureBox dari WinForms

  • menggunakan perpustakaan pihak ketiga, seperti WPFAnimatedGif dari codeplex.com.

Versi dengan PictureBoxtidak berfungsi untuk saya, dan proyek tidak dapat menggunakan perpustakaan eksternal untuk itu. Jadi saya membuatnya sendiri Bitmapdengan bantuan ImageAnimator. Karena, standar BitmapImagetidak mendukung pemutaran.gif file.

Contoh lengkap:

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

Bitmaptidak mendukung arahan URI , jadi saya memuat .giffile dari direktori saat ini.

Super AquaMen
sumber
0

Perbaikan kecil GifImage.Initialize()metode, yang membaca timing frame yang tepat dari metadata GIF.

    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;
    }
Vojta
sumber
0

Saya tidak yakin apakah ini telah dipecahkan tetapi cara terbaik adalah dengan menggunakan pustaka WpfAnimatedGid . Sangat mudah, sederhana dan mudah digunakan. Ini hanya membutuhkan 2 baris kode XAML dan sekitar 5 baris Kode C # dalam kode di belakang.

Anda akan melihat semua detail yang diperlukan tentang bagaimana ini dapat digunakan di sana. Inilah yang juga saya gunakan alih-alih menciptakan kembali roda

olammy
sumber
0

Menambahkan ke respons utama yang merekomendasikan penggunaan WpfAnimatedGif , Anda harus menambahkan baris berikut pada akhirnya jika Anda menukar gambar dengan Gif untuk memastikan animasi benar-benar dijalankan:

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

Jadi kode Anda akan terlihat seperti:

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

Periksa kode saya, saya harap ini membantu Anda :)

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

                            }
                        });
                    }

atau

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



                    }
                });
            }
pengguna10763036
sumber
0

Alternatif untuk menunggu animasi di WPF adalah:

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

Ini akan menampilkan bilah kemajuan animasi.

Marcos Kazimoto
sumber
1
Pertanyaannya tidak selalu menanyakan tentang animasi yang menunggu - itu bertanya tentang animasi GIF secara umum. Jelas, itu bisa untuk animasi yang menunggu, dalam hal ini ini mungkin menjadi alternatif yang tepat. Tapi itu bisa dengan mudah untuk sejumlah kebutuhan media lainnya.
Jeremy Caney