a perfect workaround support gif in Wpf
本文的思路是通过传统GDI+的方式显示gif图片,优点是简单方便易用,缺点是要引入System.Drawing,另外显示到UI上还是要经过GDI+的图片到WPF图片的一次转换,这一点同时带来了些微性能上的消耗。
Source Code
cs:
public class GifImage : System.Windows.Controls.Image { public string UriSource { get { return (string)GetValue(UriSourceProperty); } set { SetValue(UriSourceProperty, value); } } public static readonly DependencyProperty UriSourceProperty = DependencyProperty.Register("UriSource", typeof(string), typeof(GifImage), new FrameworkPropertyMetadata("", new PropertyChangedCallback(OnUriSourceChanged))); private static void OnUriSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { GifImage obj = (GifImage)d; obj.CreateFromSourceString((string)e.NewValue); } public Bitmap GdiPlusBitmapSource { get { return (Bitmap)GetValue(GdiPlusBitmapSourceProperty); } set { SetValue(GdiPlusBitmapSourceProperty, value); } } public static readonly DependencyProperty GdiPlusBitmapSourceProperty = DependencyProperty.Register("GdiPlusBitmapSource", typeof(Bitmap), typeof(GifImage), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnGdiPlusBitmapSourceChanged))); private static void OnGdiPlusBitmapSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { GifImage obj = (GifImage)d; obj.CreateFromBitmap((Bitmap)e.OldValue, (Bitmap)e.NewValue); } private void CreateFromBitmap(Bitmap oldSource, Bitmap source) { if (null == source) { this.Source = null; return; } if (null != oldSource) { ImageAnimator.StopAnimate(oldSource, null); } Width = GdiPlusBitmapSource.Width; Height = GdiPlusBitmapSource.Height; if (!GdiPlusBitmapSource.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Gif)) { GetBitmapSource(); return; } ImageAnimator.Animate(GdiPlusBitmapSource, OnFrameChanged); } private void CreateFromSourceString(string source) { Uri uri; try { uri = new Uri(source, UriKind.RelativeOrAbsolute); } catch (Exception) { return; } if (!uri.IsAbsoluteUri) //如果是相对Uri { if (!CreateBitmapFromStreamResource(GetFromResource(uri))) //不成功说明不在资源中 { CreateBitmapFromFile(); } return; } if (uri.GetLeftPart(UriPartial.Scheme) == "pack://") { CreateBitmapFromStreamResource(GetFromResource(uri)); return; } CreateBitmapFromFile(); } private bool CreateBitmapFromFile() { try { GdiPlusBitmapSource = Image.FromFile(UriSource) as Bitmap; } catch (Exception) { return false; } return true; } private bool CreateBitmapFromStreamResource(StreamResourceInfo streamInfo) { if (null == streamInfo) { return false; } if (!streamInfo.ContentType.Contains("image")) { return false; } try { GdiPlusBitmapSource = Image.FromStream(streamInfo.Stream) as Bitmap; } catch (Exception) { return false; } return true; } private StreamResourceInfo GetFromResource(Uri uri) { StreamResourceInfo streamInfo = Application.GetContentStream(uri); if (null == streamInfo) { streamInfo = Application.GetResourceStream(uri); } return streamInfo; } private void OnFrameChanged(object sender, EventArgs e) { Dispatcher.BeginInvoke(new Action(() => { if (null == GdiPlusBitmapSource) { return; } ImageAnimator.UpdateFrames(sender as Bitmap); GetBitmapSource(); })); } private void GetBitmapSource() { IntPtr inptr = GdiPlusBitmapSource.GetHbitmap(); try { Source = Imaging.CreateBitmapSourceFromHBitmap(inptr, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } finally { DeleteObject(inptr); } } [DllImport("gdi32")] static extern int DeleteObject(IntPtr o); }
代码是改良了网上现存的一个程序而来。
我做出的修改如下:
1. 支持显示非gif图片。
2. 加入了UriSource以及GdiPlusBitmapSource 两个DependencyProperty以便支持pack url 以及 xaml banding。
3. 可动态修改GdiPlusBitmapSource 或UriSource以切换显示图片
Usages
<gif:GifImage UriSource="c://2.gif"></gif:GifImage>
or
<gif:GifImage UriSource="2.gif"></gif:GifImage>
or
<gif:GifImage UriSource="pack://application:,,,/2.gif"></gif:GifImage>
or
<gif:GifImage x:Name="gifImage"></gif:GifImage>
codebehind: gifImage.GdiPlusBitmapSource = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromFile("c://2.gif");
note: 使用流或文件形式创建Bitmap对象时,请勿进行销毁流或删除文件、覆盖文件等操作,否则会出现GDI+错误
Performance
<ListBox x:Name="lb"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Columns="10"> </UniformGrid> </ItemsPanelTemplate> </ListBox.ItemsPanel> <ListBox.ItemTemplate> <DataTemplate> <gif:GifImage UriSource="pack://application:,,,/2.gif"></gif:GifImage> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
code behind
List<int> list = new List<int>(); for (int i = 0; i < 100; i++) { list.Add(i); } lb.ItemsSource = list;
CPU占用率从13%~16%不等
本机的配置
台式机,显卡集成。
转载请注明出处。