1. 程式人生 > >WPF播放GIF控制元件完整程式碼

WPF播放GIF控制元件完整程式碼

WPF擁有很強的介面設計能力,可以很方便的做出非常漂亮的介面。但有個問題,WPF沒有自己的播放GIF的控制元件。這讓很多想在介面播放動態動畫的人不得不使用視訊來代替。WPF就真的不能播放GIF動圖了嗎?當然是可以播放的,只是我們需要寫一些程式碼。下面是我總結後寫的一個WPF播放GIF動畫的控制元件。

下面介紹一下WPF播放GIF控制元件思路:

在WinForm裡面System.Drawing.Bitmap是可以通過ImageAnimator類來播放GIF動畫的,但是可惜的是System.Drawing.Bitmap沒有辦法直接顯示在WPF介面上。於是我們利用ImageAnimator播放System.Drawing.Bitmap對應的GIF動畫。在每次System.Drawing.Bitmap上顯示的影象改變時,獲取當前影象,然後將影象轉換為可以在WPF上顯示BitmapSource,將轉換得來的BitmapSource作為一幀動畫顯示在介面。

首先我們建立一個命名為GifImage的類,這個類繼承自System.Windows.Controls.Image。它的作用就是將System.Drawing.Bitmap轉換後得到的BitmapSource賦值給繼承自Image控制元件的Source屬性,來顯示GIF影象,當GIF每一幀的影象改變時,GifImage顯示的內容也會改變,這樣就達到了動態效果。下面是程式碼,很簡單。

public class GifImage : System.Windows.Controls.Image
    {
        /// <summary>
        /// gif動畫的System.Drawing.Bitmap
        /// </summary>
        private Bitmap gifBitmap;

        /// <summary>
        /// 用於顯示每一幀的BitmapSource
        /// </summary>
        private BitmapSource bitmapSource;

        public GifImage(string gifPath)
        {
            this.gifBitmap = new Bitmap(gifPath);
            this.bitmapSource = this.GetBitmapSource();
            this.Source = this.bitmapSource;
        }

        /// <summary>
        /// 從System.Drawing.Bitmap中獲得用於顯示的那一幀影象的BitmapSource
        /// </summary>
        /// <returns></returns>
        private BitmapSource GetBitmapSource()
        {
            IntPtr handle = IntPtr.Zero;

            try
            {
                handle = this.gifBitmap.GetHbitmap();
                this.bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, System.Windows.Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                if (handle != IntPtr.Zero)
                {
                    DeleteObject(handle);
                }
            }

            return this.bitmapSource;
        }

        /// <summary>
        /// Start animation
        /// </summary>
        public void StartAnimate()
        {
            ImageAnimator.Animate(this.gifBitmap, this.OnFrameChanged);
        }

        /// <summary>
        /// Stop animation
        /// </summary>
        public void StopAnimate()
        {
            ImageAnimator.StopAnimate(this.gifBitmap, this.OnFrameChanged);
        }

        /// <summary>
        /// Event handler for the frame changed
        /// </summary>
        private void OnFrameChanged(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=> 
            {
                ImageAnimator.UpdateFrames();  // 更新到下一幀
                if (this.bitmapSource != null)
                {
                    this.bitmapSource.Freeze();
                }

                //// Convert the bitmap to BitmapSource that can be display in WPF Visual Tree
                this.bitmapSource = this.GetBitmapSource();
                Source = this.bitmapSource;
                this.InvalidateVisual();
            }));
        }

        /// <summary>
        /// Delete local bitmap resource
        /// Reference: http://msdn.microsoft.com/en-us/library/dd183539(VS.85).aspx
        /// </summary>
        [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool DeleteObject(IntPtr hObject);

    }

其中ImageAnimator類是通過靜態方法Animate和StopAnimate開始和結束System.Drawing.Bitmap所對應的GIF動畫。這兩個函式的第二個引數是一個委託,當前播放GIF動畫每一幀影象發生改變的時候,這個委託所指定的函式就會被觸發。那麼我們就可以在這個委託裡將當前顯示的System.Drawing.Bitmap影象轉換為在WPF上可以顯示的BitmapSource,然後賦值給Source屬性顯示在介面。

下面是在一個Window中使用GIFInage控制元件。ProgressIndicator.gif是要播放的GIF檔案的檔名,在程式執行目錄。

public partial class MainWindow : Window
    {
        private GifImage gifImage; 

        public MainWindow()
        {
            InitializeComponent();
            this.gifImage = new GifImage("ProgressIndicator.gif");
            this.gifImage.Width = 100;
            this.gifImage.Height = 100;
            this.Content = this.gifImage;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.gifImage.StartAnimate();
        }
    }