WPF에서 애니메이션 GIF를 사용하려면 어떻게해야합니까?


218

어떻게 제어 유형 사용한다 - Image, MediaElement등?


4
아래 솔루션에 대한 최근 요약이 있습니다. VS2015를 사용하여 구현했습니다. Dario가 제출 한 GifImage 클래스는 훌륭했지만 내 gif 중 일부는 아티팩트되었습니다. Pradip Daunde와 nicael의 MediaElement 접근 방식은 미리보기 영역에서 작동하는 것으로 보이지만 런타임 중에 내 GIF는 렌더링되지 않습니다. IgorVaschuk과 SaiyanGirl의 WpfAnimatedGif 솔루션은 문제없이 훌륭하게 작동했지만 타사 라이브러리를 설치해야합니다 (분명히). 나는 나머지를 시도하지 않았다.
Heath Carroll

답변:


214

이 질문에 가장 인기있는 답변을 얻지 못했습니다 (Dario 위). 결과는 이상한 아티팩트로 이상하고 고르지 않은 애니메이션이었습니다. 내가 지금까지 찾은 최고의 솔루션 : https://github.com/XamlAnimatedGif/WpfAnimatedGif

NuGet으로 설치할 수 있습니다

PM> Install-Package WpfAnimatedGif

gif 이미지를 추가하고 아래와 같이 창에 새 네임 스페이스에서 사용하십시오.

<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의 의견에 따라 Silverlight 프로젝트에 애니메이션 GIF 지원을 추가하려면 github.com/XamlAnimatedGif/XamlAnimatedGif 를 사용 하십시오


13
이것은 훌륭하게 작동했으며 구현하는 데 60 초도 걸리지 않았습니다. 감사!
Ryan Sorensen

3
특히 인기있는 IMO보다 더 나은 답변, 특히 C #을 사용하지 않기 때문에 답변
Jamie E

8
이것은 허용 된 답변보다 훨씬 낫습니다 : gif의 메타 데이터를 사용하고, 고르지 않으며, NuGet 패키지이며 언어에 구애받지 않습니다. 수락 된 답변에 대한 확신이없는 투표에 스택 오버 플로우가 허용되기를 바랍니다.
John Gietzen

6
공공 서비스 발표 : WpfAnimatedGif의 저자는 '재부팅'XamlAnimatedGif 그의 프로젝트, 그것은 WPF, 윈도우 스토어 (Win8), 윈도우 10, 실버 라이트를 지원했습니다 github.com/XamlAnimatedGif/XamlAnimatedGif
josh2112

2
img여기 가 무엇입니까 ?
amit jha

104

이미지 컨트롤을 확장하고 Gif Decoder를 사용하는 솔루션을 게시했습니다. gif 디코더에는 프레임 속성이 있습니다. FrameIndex속성을 애니메이션합니다 . 이 이벤트 ChangingFrameIndex는 source 속성을 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 Animated GIF 라이브러리가이를 적절히 고려합니다.
Thomas Levesque

38

나도 검색을하고 오래된 MSDN 포럼의 스레드에서 여러 가지 다른 솔루션을 찾았습니다. (링크가 더 이상 작동하지 않아서 제거했습니다)

가장 간단한 실행은 WinForms를 사용하는 것 같습니다 PictureBox 컨트롤 은 스레드에서 몇 가지 사항을 변경했습니다. 대부분 동일합니다.

에 대한 참조를 추가 System.Windows.Forms, WindowsFormsIntegration그리고 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"를 넣을 수 있습니까?
Junior Mayhé 2009

@ 주니어 : 예, 설정할 수 있습니다 AllowTransparency="True". 그것이 당신이 생각하는 결과를 낳을 것인지 아닌지는 또 다른 문제입니다. 나는 그것을 직접 시도하지는 않았지만 WindowsFormsHost전혀 투명하지 않을 것이라고 확신했다 . 나머지 Window힘. 당신은 단순히 그것을 시도해야한다고 생각합니다.
Joel B Fant

winform API로 인해 pictureBoxLoading.Image에 문제가있었습니다. 내 문제를 해결하는 코드를 아래에 게시했습니다. 솔루션 주셔서 감사합니다, Joel!
sondlerd

당신의 모습이 죽은 것 같습니다. 이 되었습니까 이 스레드 ?
wip

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

2
최고의 답변 ... 맨 위에 있어야합니다! 다만이 - 난 뒤에 코드없이 작업 얻을 수있었습니다 <MediaElement LoadedBehavior="Play" Source="{Binding MyGifFile}" >- MyGifFile 내 애니메이션 GIF의 단지 파일 이름 (경로)입니다.
Anthony Nichols

Jeez, 왜에 묶 ListBox거나 전혀 묶지 않습니까? 바인딩하지 않고 시도했지만 소스에 파일 경로를 넣으면 표시되지만 애니메이션되지는 않습니다. 바인딩을 사용하더라도조차도 ListBox전혀 나오지 않습니다. 나에게 표시 될 때 사용하는 것과 동일하더라도 파일 경로가 잘못되었다는 예외가 발생합니다.
vapcguy

업데이트하는 데 시간이 오래 걸리며 볼 때마다 업데이트해야합니다.
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로 설정하고 Window가 있던 폴더의 상대 경로를 사용하여 Source를 설정했습니다 (예 : Source = "../../ Images / Rotating-e.gif"). 나를 위해 잘 작동했으며 타사 DLL이 필요하지 않습니다.
Richard Moore

이것은 가장 간단한 솔루션입니다. 그러나 문제는 애니메이션 GIF의 모든 프레임이 스캔되면 애니메이션이 중지된다는 것입니다. 그리고 프레임 0에서 gif를 다시 애니메이션으로 만드는 방법은 없습니다. 애니메이션을 다시 시작하거나 영원히 반복 할 수 없습니다. 적어도 <MediaElement />를 사용하는 방법을 찾지 못했습니다.
보이시 베이크

또한 <MediaElement />는 믿을 수 없을 정도로 느리고 메소드 간 스레드 레이싱 문제로 가득합니다. Grrr….
BoiseBaked

10

다음은 애니메이션 이미지 컨트롤의 내 버전입니다. 이미지 소스를 지정하기 위해 표준 특성 소스를 사용할 수 있습니다. 나는 그것을 더 향상시켰다. 나는 러시아어, 프로젝트는 러시아어이므로 의견도 러시아어입니다. 그러나 어쨌든 당신은 의견없이 모든 것을 이해할 수 있어야합니다. :)

/// <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
소닉 소울

훌륭하게, 나는 리소스 사전-> BitmapImage-> 애니메이션 GIF의 GIF를 제외하고는 가능한 솔루션이 필요했습니다. 이거 야!
mtbennett

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 솔루션이지만 이번에는 코드 숨김을 사용하여 프로젝트에서 Embedded Resource를 사용합니다.

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

WPF를 사용하는 주된 이유 중 하나는 디스플레이 스케일링 때문이기 때문에 이것이 좋은 생각이라고 생각하지 않습니다. 제대로 확장되지 않은 하나의 아티팩트 (이미지)가 생깁니다.
머핀 맨

5

Mike Eshva의 코드를 수정하고 더 잘 작동하도록 만들었습니다 .1frame jpg png bmp 또는 mutil-frame gif와 함께 사용할 수 있습니다. 컨트롤에 uri를 바인딩하려면 UriSource 속성을 바인딩하거나 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
}

이것은 사용자 정의 컨트롤입니다. WPF App Project에서 생성하고 스타일에서 템플릿 재정의를 삭제해야합니다.


1
방금 UriSource를 pack : // application : ,,, / Images / loader.gif로 설정해야했습니다. 런타임시 UriSource 또는 Source를 상대 Uri로 설정하지 못했습니다.
Farzan

예, 시도했지만 예외가 발생합니다. 상대 소변으로는 작동하지 않습니다.
SuperJMN

3

WPF4에서 자체 키 프레임 이미지 애니메이션을 시뮬레이션 할 수 있음을 발견 할 때까지이 문제가있었습니다. 먼저 애니메이션을 일련의 이미지로 분할하고 "Image1.gif", "Image2, gif"등과 같은 제목을 붙입니다. 해당 이미지를 솔루션 리소스로 가져옵니다. 이미지의 기본 리소스 위치에 배치한다고 가정합니다.

이미지 컨트롤을 사용하려고합니다. 다음 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
이 방법의 단점 중 하나는 기본적으로 애니메이션이 축소 된 후에도 애니메이션이 계속되어 성능이 저하 될 수 있다는 것입니다.
Lynn

DiscreteObjectKeyFrames가 아니며 DiscreteObjectKeyFrame입니다. 단수형.
jairhumberto

@ jairhumberto 버전마다 변경되었을 수 있습니다. 이것은 꽤 오래되었지만 (2011) 실제로 프로젝트 에서이 정확한 코드를 사용하고있었습니다.
CodeMouse92

3

귀하의 게시물 Joel에 감사 드리며 WPF의 애니메이션 GIF 지원 부재를 해결하는 데 도움이되었습니다. Winforms API로 인해 pictureBoxLoading.Image 속성을 설정하는 데 시간이 많이 걸리기 때문에 약간의 코드를 추가하는 것입니다.

애니메이션 GIF 이미지의 Build Action을 "Content"로 설정하고 Copy to output 디렉토리를 "New if if newer"또는 "always"로 설정해야했습니다. 그런 다음 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에 따르면 스트림 을 사용하는 비트 맵은 비트 맵 수명 동안 스트림을 활성 상태로 유지해야합니다. 해결 방법은 비트 맵을 고정하거나 복제하는 것입니다.
Jesse Chisholm

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하지만 그렇지 않으면 챔피언처럼 작동했습니다!
Steve Danner

1
GC.Collect ()를 명시 적으로 호출하면 성능에 끔찍한 영향을 미칩니다.
Kędrzu

0

이전에는 비슷한 문제에 직면 .gif하여 프로젝트에서 파일 을 재생 해야했습니다. 두 가지 선택이있었습니다.

  • WinForms에서 PictureBox 사용

  • WPFAnimatedGif와 같은 타사 라이브러리 사용 codeplex.com의

with 버전이 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.