アニメーションGIFをWPFで機能させるにはどうすればよいですか?


217

-私はどのようなコントロールタイプを使用する必要がありますImageMediaElementなど、?


4
以下は、以下のソリューションの最近の要約です。VS2015を使用してこれらを実装しました。Darioから送信されたGifImageクラスはうまく機能しましたが、私のgifの一部はアーティファクトでした。Pradip DaundeとnicaelによるMediaElementアプローチはプレビュー領域で機能するようですが、実行時にレンダリングされる私のgifはどれも動作しません。IgorVaschukとSaiyanGirlによるWpfAnimatedGifソリューションは問題なく機能しましたが、サードパーティのライブラリをインストールする必要があります(明らかに)。残りは試しませんでした。
ヒースキャロル

回答:


214

この質問に対する最も一般的な回答(上記のDarioによる)が適切に機能しませんでした。その結果、奇妙なアーティファクトを含む奇妙で途切れたアニメーションができました。これまでに見つけた最善の解決策:https : //github.com/XamlAnimatedGif/WpfAnimatedGif

NuGetでインストールできます

PM> Install-Package WpfAnimatedGif

それを使用するには、gif画像を追加するWindowの新しい名前空間で、以下のように使用します。

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

パッケージは本当に素晴らしいです、あなたは以下のようないくつかの属性を設定することができます

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

コードでも使用できます。

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

編集:Silverlightサポート

josh2112のコメントに従って、アニメーションGIFサポートをSilverlightプロジェクトに追加する場合は、github.com / XamlAnimatedGif / XamlAnimatedGifを使用します


13
これはうまく機能し、実装に60秒もかかりませんでした。ありがとう!
ライアンソレンセン2012

3
特にC#を使用していることに依存していないため、人気のあるIMOのどれよりもはるかに良い回答
Jamie E

8
これは、受け入れられた回答よりもはるかに優れています。gifのメタデータを使用し、途切れがなく、NuGetパッケージであり、言語に依存しません。stackoverflowが、受け入れられた回答に自信のない投票を許可することを願っています。
John Gietzen 2013年

6
公共サービスの発表:WpfAnimatedGifの作成者は、プロジェクトをXamlAnimatedGifとして「再起動」し、WPF、Windowsストア(Win8)、Windows 10、およびSilverlightをサポートしています: github.com/XamlAnimatedGif/XamlAnimatedGif
josh2112

2
imgここは何ですか?
amit jha 2018年

104

画像コントロールを拡張し、Gif Decoderを使用するソリューションを投稿します。gifデコーダーには、framesプロパティがあります。FrameIndexプロパティをアニメーション化します。イベントChangingFrameIndexは、ソースプロパティを対応するFrameIndex(デコーダー内の)フレームに変更します。私はgifが毎秒10フレームを持っていると思います。

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

使用例(XAML):

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

1
追加の参照が必要ないため、これは機能し、XBAPアプリに適しています。
Max Galkin

1
カッコいい。コンストラクターコードを「Initialized」イベントに配置し、Uriプロパティを導入することで、このコントロールをXAMLファイルに配置することもできます。
flq

1
+1、いいね!ただし、画像の実際のフレーム期間は考慮されません...その情報を読み取る方法を見つけることができる場合は、コードを変更して次のように使用できますInt32AnimationUsingKeyFrames
Thomas Levesque

7
実際、フレームレートはGIFで一定なので、結局キーフレームは必要ありません...フレームレートはgf.Frames[0].MetaData.GetQuery("/grctlext/Delay")(フレーム長が数百秒であるushortを返します)
Thomas Levesque

3
@vidstige、はい、そのとき(ほぼ2年前)にこのコメントを書いた理由を思い出しません。遅延はフレームごとに異なる可能性があることを承知しており、私のWPFアニメーションGIFライブラリはそれを適切に考慮します。
Thomas Levesque 2012年

38

私も検索して、古いMSDNフォーラムのスレッドでいくつかの異なるソリューションを見つけました。(リンクが機能しなくなったので削除しました)

実行するのが最も簡単なのは、WinForms PictureBoxコントロールを使用することであり、このようになりました(スレッドからいくつかの点を変更しましたが、ほとんどは同じです)。

参照を追加System.Windows.FormsWindowsFormsIntegrationと、System.Drawing最初のプロジェクトにします。

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

次に、Window_LoadedハンドラーでpictureBoxLoading.ImageLocation、表示する画像ファイルのパスにプロパティを設定します。

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

MediaElement制御は、そのスレッドに記載されたが、に基づいて、少なくとも2つの自家製コントロールを含む、代替案の数があったので、また、それがかなり重い制御であることが記載されているImage制御これは最も単純であるので、。


WindowsFormsHostを使用するときに、AllowTransparency = "True"でこのメインウィンドウを配置できますか?
ジュニアメイヘ2009

@ジュニア:ええ、設定できますAllowTransparency="True"。それがあなたが考えている結果を生み出すかどうかは別の問題です。私自身は試してWindowsFormsHostいませんが、まったく透明にならないと思います。残りのWindow力。あなたは単にそれを試さなければならないだろうと思います。
ジョエルBファント

winform APIが原因で、pictureBoxLoading.Imageで問題が発生しました。以下のコードを投稿して問題を解決しました。解決策をありがとう、ジョエル!
sondlerd '06 .06.29

あなたのようなものが死んでいるように見えます。それはだったこのスレッド
wip 2013

2
統合参照を追加するとき、私のUIでの名前はドットのないWindowsFormsIntegrationです:i.imgur.com/efMiC23.png
yu yang Jian

36

この小さなアプリはどうですか:コードビハインド:

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
いいね!短いコードで、うまく機能しています。それ以上の投票がないとは信じられません。
wip 2013

2
ベストアンサー...一番上にあるはずです!ただ、これは-私はそれが背後にある任意のコードなしで動作させることができました<MediaElement LoadedBehavior="Play" Source="{Binding MyGifFile}" >- MyGifFileは私のアニメーションGIFのファイル名のみ(とパス)です。
Anthony Nichols

うわあ、どうしてにバインドするListBoxか、まったくバインドする必要がないのですか?バインドせずに試しましたが、ファイルパスをSourceに置くだけで表示されますが、アニメーションはしません。バインディングを使用ListBoxしても、私にとってはまったく表示されません-表示されたときに使用したものと同じであっても、ファイルパスが正しくないという例外が発生します。
vapcguy 2016年

更新には時間がかかり、表示されるたびに更新する必要があります。
Yola、

15

あなたが使用する場合、それは非常に簡単です<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" />

ファイルがアプリにパッケージ化されている場合に備えて、ソースにDataBindingを使用し、コードでパスを見つけることができますpublic string SpinnerLogoPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\images\mso_spinninglogo_blue_2.gif");。ファイルをBuild = Contentに設定し、出力ディレクトリにコピーしてください。
マフィンマン

WpfAnimatedGif NuGetパッケージがうまく機能しなかったため、この方法を使用しました。CPUの負荷が高いときに問題が発生したようです。私はGIFをBuild = Resourceに設定し、ソースがウィンドウにあったフォルダーからの相対パスを使用してソースを設定します(例:Source = "../../ Images / Rotating-e.gif")。私には問題なく動作し、サードパーティのDLLは必要ありません。
Richard Moore

これは、これまでで最も簡単なソリューションです。しかし、問題は、アニメーションGIFのすべてのフレームがスキャンされると、アニメーションが停止することです。また、gifをフレーム0から再びアニメーション化する方法はありません。アニメーションを再開したり、永久にループしたりする方法はありません。少なくとも、<MediaElement />を使用する方法は見つかりませんでした。
BoiseBaked

また、<MediaElement />は信じられないほど遅く、そのメソッド間のスレッド競合の問題でいっぱいです。Grrr…。
BoiseBaked

10

これがアニメーションイメージコントロールの私のバージョンです。画像のソースを指定するには、標準のプロパティSourceを使用できます。さらに改善しました。私はロシア人です。プロジェクトはロシア語なので、コメントもロシア語です。しかし、とにかくコメントなしですべてを理解できるはずです。:)

/// <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
このコードは私のプロジェクトの一部です。私はロシアで働くロシアの開発者です。コメントもロシア語です。世界のすべてのプロジェクトが「アメリカ英語」のプロジェクト、コーリーであるとは限りません。
Mike Eshva

2
次のマークアップでコードを使用してみました:<local:AnimatedImage Source = "/ Resources / ajax-loader.gif" />しかし、これまでのところ何も起こりません
Sonic Soul

jpegを使用するように変更すると、静止画像が表示されます。ただgifではありません。ニースコードBTW
Sonic Soul

素晴らしい、私はリソース辞書からのGIFを除くことができるソリューションが必要でした-> BitmapImage->アニメーションGIF。これだよ!
mtbennett 2014年

9

私はこのライブラリを使用します:https : //github.com/XamlAnimatedGif/WpfAnimatedGif

最初に、(パッケージマネージャーコンソールを使用して)ライブラリをプロジェクトにインストールします。

    PM > Install-Package WpfAnimatedGif

次に、このスニペットを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" />
        ...

お役に立てば幸いです。

ソース:https : //github.com/XamlAnimatedGif/WpfAnimatedGif


3
これは、2012年6月の@IgorVaschukの回答と同じ(あまり詳細ではありません)の回答と同じです。現在、投票で2位のソリューションです。
ヒースキャロル

5

基本的に上記のPictureBoxソリューションと同じですが、今回はプロジェクトで埋め込みリソースを使用するためのコードビハインドを使用します。

XAMLの場合:

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

コードビハインドで:

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

良い追加。私が言えることから、本当にそれを合理化します。(とはいえ、現在3年以上WPFで記述していません。)
CodeMouse92 2013年

WPFを使用する主な理由の1つはディスプレイのスケーリングにあるため、これは良い考えではないと私は思います。適切にスケーリングされない1つのアーチファクト(画像)が作成されます。
マフィンマン

5

Mike Eshvaのコードを変更し、より適切に機能させました。1framejpg png bmpまたはmutil-frame gifのいずれかで使用できます。uriをコントロールにバインドする場合は、UriSourceプロパティをバインドするか、任意のBitmapImageであるSourceプロパティをバインドするメモリストリーム。

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

これはカスタムコントロールです。これをWPFアプリプロジェクトで作成し、テンプレートオーバーライドをスタイルで削除する必要があります。


1
UriSourceをpack:// application:,,, / Images / loader.gifに設定する必要がありました。UriSourceまたはSourceを相対Uriに設定すると、実行時に失敗しました。
Farzan 2013年

はい、試しましたが、例外が発生します。相対URIでは機能しません。
SuperJMN 2016年

3

WPF4では、独自のキーフレームイメージアニメーションをシミュレートできることを発見するまで、この問題がありました。まず、アニメーションを一連の画像に分割し、「Image1.gif」、「Image2、gif」などのタイトルを付けます。これらの画像をソリューションリソースにインポートします。私はそれらを画像のデフォルトのリソースの場所に置くことを想定しています。

Imageコントロールを使用します。次のXAMLコードを使用します。必須でないものは削除しました。

<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
このアプローチの欠点の1つは、デフォルトではアニメーションが折りたたまれた後も継続するため、パフォーマンスが低下する可能性があることです。
Lynn

それはDiscreteObjectKeyFramesではなく、DiscreteObjectKeyFrameです。特異な。
jairhumberto

@jairhumbertoバージョン間で変更された可能性があります。これはかなり古い(2011)ですが、プロジェクトで実際にこの正確なコードを使用していました。
CodeMouse92

3

Joel氏の投稿に感謝します。アニメーションGIFに対するWPFのサポートの欠如を解決するのに役立ちました。Winforms APIのために、pictureBoxLoading.Imageプロパティを設定するのにかなり時間がかかったので、ちょっとしたコードを追加するだけです。

アニメーションgif画像のビルドアクションを「コンテンツ」に設定し、「出力ディレクトリにコピー」を「新しい場合は常にコピー」または「常に」に設定する必要がありました。次に、MainWindow()でこのメソッドを呼び出しました。唯一の問題は、ストリームを処分しようとしたときに、イメージの代わりに赤い封筒のグラフィックが表示されたことです。その問題を解決しなければならない。これにより、BitmapImageをロードしてBitmapに変更するという痛みが取り除かれました(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);
   }
}

re:ストリームを破棄しようとしたとき、MSDNによると、ストリームを使用するビットマップは、ビットマップの存続期間中、ストリームを存続させる必要があります。回避策は、ビットマップをフリーズまたはクローンすることです。
ジェシーチザム

1
彼はの.ImageLocation代わりに設定するように言う必要がありました.Image。彼は間違った方法を持っていました。.ImageLocationVisual Studioプロジェクトのルートから機能するため、Imagesフォルダーがあるとすると、パスはになりimgBox.ImageLocation = "/Images/my.gif";ます。Views画像を表示するビューがある場所と呼ばれるフォルダがある場合、に戻るにはImages、2つのドットを使用する必要がありますimgBox.ImageLocation = "../Images/my.gif";
vapcguy

1

私は上記のすべての方法を試しましたが、それぞれに短さがあり、皆さんのおかげで、私は自分のGifImageを計算しています:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Controls;
    using System.Windows;
    using System.Windows.Media.Imaging;
    using System.IO;
    using System.Windows.Threading;

    namespace IEXM.Components
    {
    public class GifImage : Image
    {
            #region gif Source, such as "/IEXM;component/Images/Expression/f020.gif"
            public string GifSource
            {
                    get { return (string)GetValue(GifSourceProperty); }
                    set { SetValue(GifSourceProperty, value); }
            }

            public static readonly DependencyProperty GifSourceProperty =
                    DependencyProperty.Register("GifSource", typeof(string),
                    typeof(GifImage), new UIPropertyMetadata(null, GifSourcePropertyChanged));

            private static void GifSourcePropertyChanged(DependencyObject sender,
                    DependencyPropertyChangedEventArgs e)
            {
                    (sender as GifImage).Initialize();
            }
            #endregion

            #region control the animate
            /// <summary>
            /// Defines whether the animation starts on it's own
            /// </summary>
            public bool IsAutoStart
            {
                    get { return (bool)GetValue(AutoStartProperty); }
                    set { SetValue(AutoStartProperty, value); }
            }

            public static readonly DependencyProperty AutoStartProperty =
                    DependencyProperty.Register("IsAutoStart", typeof(bool),
                    typeof(GifImage), new UIPropertyMetadata(false, AutoStartPropertyChanged));

            private static void AutoStartPropertyChanged(DependencyObject sender,
                    DependencyPropertyChangedEventArgs e)
            {
                    if ((bool)e.NewValue)
                            (sender as GifImage).StartAnimation();
                    else
                            (sender as GifImage).StopAnimation();
            }
            #endregion

            private bool _isInitialized = false;
            private System.Drawing.Bitmap _bitmap;
            private BitmapSource _source;

            [System.Runtime.InteropServices.DllImport("gdi32.dll")]
            public static extern bool DeleteObject(IntPtr hObject);

            private BitmapSource GetSource()
            {
                    if (_bitmap == null)
                    {
                            _bitmap = new System.Drawing.Bitmap(Application.GetResourceStream(
                                     new Uri(GifSource, UriKind.RelativeOrAbsolute)).Stream);
                    }

                    IntPtr handle = IntPtr.Zero;
                    handle = _bitmap.GetHbitmap();

                    BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                            handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                    DeleteObject(handle);
                    return bs;
            }

            private void Initialize()
            {
            //        Console.WriteLine("Init: " + GifSource);
                    if (GifSource != null)
                            Source = GetSource();
                    _isInitialized = true;
            }

            private void FrameUpdatedCallback()
            {
                    System.Drawing.ImageAnimator.UpdateFrames();

                    if (_source != null)
                    {
                            _source.Freeze();
                    }

               _source = GetSource();

              //  Console.WriteLine("Working: " + GifSource);

                    Source = _source;
                    InvalidateVisual();
            }

            private void OnFrameChanged(object sender, EventArgs e)
            {
                    Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(FrameUpdatedCallback));
            }

            /// <summary>
            /// Starts the animation
            /// </summary>
            public void StartAnimation()
            {
                    if (!_isInitialized)
                            this.Initialize();


             //   Console.WriteLine("Start: " + GifSource);

                    System.Drawing.ImageAnimator.Animate(_bitmap, OnFrameChanged);
            }

            /// <summary>
            /// Stops the animation
            /// </summary>
            public void StopAnimation()
            {
                    _isInitialized = false;
                    if (_bitmap != null)
                    {
                            System.Drawing.ImageAnimator.StopAnimate(_bitmap, OnFrameChanged);
                            _bitmap.Dispose();
                            _bitmap = null;
                    }
                    _source = null;
                    Initialize();
                    GC.Collect();
                    GC.WaitForFullGCComplete();

             //   Console.WriteLine("Stop: " + GifSource);
            }

            public void Dispose()
            {
                    _isInitialized = false;
                    if (_bitmap != null)
                    {
                            System.Drawing.ImageAnimator.StopAnimate(_bitmap, OnFrameChanged);
                            _bitmap.Dispose();
                            _bitmap = null;
                    }
                    _source = null;
                    GC.Collect();
                    GC.WaitForFullGCComplete();
               // Console.WriteLine("Dispose: " + GifSource);
            }
    }
}

使用法:

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

メモリリークが発生せず、gif画像自体のタイムラインをアニメーション化したので、試してみることができます。


優れたサンプル。をチェックするためIsAutoStartに更新された初期化が必要ですが、それ以外の点では、チャンピオンのように機能しました!
スティーブダナー2014

1
GC.Collect()を明示的に呼び出すと、パフォーマンスに恐ろしい影響があります。
Kędrzu

0

以前、私は同様の問題に直面.gifし、プロジェクトでファイルを再生する必要がありました。私には2つの選択肢がありました。

  • WinFormsからPictureBoxを使用する

  • codeplex.comのWPFAnimatedGifなどのサードパーティライブラリを使用する

を使用したバージョンPictureBoxは私にとっては機能せず、プロジェクトは外部ライブラリを使用できませんでした。それで私はBitmap助けを借りて自分のためにそれを作りましたImageAnimator。なぜなら、標準BitmapImage.gifファイルの再生をサポートしていないからです。

完全な例:

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ディレクティブをサポートしていないため.gif、現在のディレクトリからファイルをロードします。


0

GifImage.Initialize()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;
    }

0

これが解決されたかどうかはわかりませんが、最善の方法はWpfAnimatedGidライブラリを使用することです。使い方はとても簡単でシンプルで簡単です。2行のXAMLコードと約5行のC#コードのみがコードビハインドに必要です。

ここでこれをどのように使用できるかについて必要な詳細がすべて表示されます。これは、ホイールを再発明する代わりに使用したものです


0

WpfAnimatedGifの使用を推奨するメインレスポンスに追加して、アニメーションが実際に実行されるように画像をGifと交換する場合は、最後に次の行を追加する必要があります。

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

したがって、コードは次のようになります。

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

私のコードを確認してください、これがあなたの役に立ったと思います:)

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

                            }
                        });
                    }

または

     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で待機中のアニメーションの代替手段は次のとおりです。

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

アニメーション化された進行状況バーが表示されます。


1
質問は必ずしも待機中のアニメーションについて尋ねるのではなく、一般にアニメーションGIFについて尋ねています。明らかに、それ待機中のアニメーションの可能性があります。その場合、これは適切な代替手段になる可能性があります。しかし、他のメディアのニーズに対しても同じくらい簡単にできます。
Jeremy Caney
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.