本文的思路是通过传统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%不等
本机的配置
台式机,显卡集成。
转载请注明出处。