Animated Image
(움직이는 이미지)


Intro

 WPF 에서 많이 사용하는 ImageSource 의 경우, AnimatedImage 를 지원하지 않습니다. 소위 말하는 '움짤'을 표시할 수 없다라는 것이죠. 고정된 이미지로 보여집니다. 현재로서는 이를 지원하기 위해서 구현을 해 주어야 하지요.


Content

구현하는 방법은 대략 이렇습니다. BitmapDecoder 를 이용하여 Image 의 Frame 들을 얻어옵니다. 여기서 Frame 이란 Animated Image 의 각 장면들을 말합니다. 이렇게 얻은 Frame 을 일정 주기마다 화면에 출력합니다. 간단하죠? 코드는 아래와 같습니다.


public class AnimatedImage : Image
{
    BitmapDecoder decoder = null;
    private int Count = 0;
    private System.Timers.Timer Timer = new System.Timers.Timer();
    private object TimerLock = new object();

    public double Interval { get { return this.Timer.Interval; } set { this.Timer.Interval = value; } }
    public bool IsPlaying { get; set; }

    public AnimatedImage()
        : base() {
        this.IsPlaying = false;
        this.Interval = 100;
        this.Timer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed);
    }




    //------------------------- 
    // Image 변경되었을 경우 
    //------------------------- 
    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) {
        if (e.Property.Name == "Source") {
            if (e.NewValue != null &&
                (e.OldValue == null || (e.NewValue.ToString() != e.OldValue.ToString()))) {
                this.Stop();
                try {
                    decoder = new BitmapDecoder(new Uri(e.NewValue.ToString()),
                        BitmapCreateOptions.None, BitmapCacheOption.None);
                } catch { }
                this.Start();
            }
        }
        base.OnPropertyChanged(e);
    }



    //------------------------- 
    // Start Animation 
    //------------------------- 
    private void Start() {
        lock (TimerLock) {
            if (this.decoder != null && this.decoder.Frames.Count > 1) {
                if (this.IsPlaying == false) {
                    this.IsPlaying = true;
                    this.Timer.Start();
                }
            }
        }
    }



    //------------------------- 
    // Stop Animation 
    //------------------------- 
    private void Stop() {
        lock (TimerLock) {
            this.IsPlaying = false;
            this.Timer.Stop();
        }
    }



    //------------------------- 
    // Timer Procedure 
    //------------------------- 
    void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
        this.Dispatcher.BeginInvoke(new Action(delegate {
            Count = ++Count % decoder.Frames.Count;
            this.Source = decoder.Frames[Count];
            this.InvalidateArrange();
        }), null);
    }
} 




이 코드에는 해결해야 하는 문제가 조금 남아 있습니다.

1. Image 의 Source 에 이미지의 uri 정보가 있어야만 동작합니다.
 - Image 에 Stream 을 통해서 만든 ImageSource 나 BitmapImage 등이 들어가면, BitmapDecoder 를 생성하지 못합니다.
 - 즉, Source 에 들어오는 Object Type 별로 BitmapDecoder 를 생성하는 루틴을 추가해야 합니다.

2. WPF 의 고질적인 Image 에 Uri 가 들어갈 경우, 이미지 파일 락(File Lock)이 걸립니다.
 - WPF 에서 ImageSource 를 Uri 를 통해 만들었을 때, File Handle 을 Release 하는 방법을 적용하면 됩니다.
 - 이는 기존에 WPF Image 에 존재하는 문제입니다.


사용법은 다음과 같습니다.


<font face="Arial Black"><AnimatedImage Source="C:\crystalcube.gif" Interval="100"/> </font>


Interval 에 따라서 Animation 의 속도가 변경됩니다. 보통 100 정도면 적당하더군요.


Result

 혹 위에 언급한 두가지 문제를 해결하는 방법을 아시면~ 공유 부탁합니다 ^-^


'Microsoft > WPF' 카테고리의 다른 글

TypeConverter  (0) 2014.05.12
XAML 의 문법구조  (0) 2014.05.12
xaml 과 cs 파일의 관계  (1) 2014.05.08
WPF 기본 동작구조  (0) 2014.05.08
WPF 3D Tutorial  (7) 2011.04.09