WPF自定义控件与样式(12)

WPF自定义控件与样式(12)
WPF自定义控件与样式(12)

WPF自定义控件与样式(12)

WPF自定义控件与样式(12)-缩略图ThumbnailImage /gif动画图/图片列表

时间2015-12-01 09:21:00

博客园-原创精华区原文

https://www.360docs.net/doc/0710983240.html,/anding/p/5009120.html

主题

WPF

Bitmap一.前言

申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接。

本文主要针对WPF项目开发中图片的各种使用问题,经过总结,把一些经验分享一下。内容包括:

WPF常用图像数据源ImageSource的创建;

自定义缩略图控件ThumbnailImage,支持网络图片、大图片、图片异步加载等特性;

动态图片gif播放控件;

图片列表样式,支持大数据量的虚拟化;

二.WPF常用图像数据源ImageSource的创建

<Image Source="../Images/qq.png"></Image>

这是一个普通Image控件的使用,Source的数据类型是ImageSource,在XAML中可以使用文件绝对路径或相对路径,ImageSource是一个抽象类,我们一般使用BitmapSource、BitmapImage等。

但在实际项目中,有各种各样的需求,比如:从Bitmap创建ImageSource对象;

从数据流byte[]创建ImageSource对象;

从System.Drawing.Image创建ImageSource对象;

从一个大图片文件创建一个指定大小的ImageSource对象;

2.1 从System.Drawing.Image创建指定大小ImageSource 对象

/// <summary>

/// 使用System.Drawing.Image创建WPF使用的ImageSource类型缩略图(不放大小图)

/// </summary>

/// <param

name="sourceImage">System.Drawing.Image 对象

</param>

/// <param name="width">指定宽度</param>

/// <param name="height">指定高度</param> public static ImageSource CreateImageSourceThumbnia(System.Drawing.Image sourceImage, double width, double height)

{

if (sourceImage == null) return null;

double rw = width / sourceImage.Width;

double rh = height / sourceImage.Height;

var aspect = (float)Math.Min(rw, rh);

int w = sourceImage.Width, h = sourceImage.Height;

if (aspect < 1)

{

w = (int)Math.Round(sourceImage.Width * aspect); h = (int)Math.Round(sourceImage.Height * aspect);

}

Bitmap sourceBmp = new Bitmap(sourceImage, w, h); IntPtr hBitmap = sourceBmp.GetHbitmap(); BitmapSource bitmapSource =

Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty,

BitmapSizeOptions.FromEmptyOptions()); bitmapSource.Freeze();

System.Utility.Win32.Win32.DeleteObject(hBitmap); sourceImage.Dispose();

sourceBmp.Dispose();

return bitmapSource;

}

2.2 从一个大图片文件创建一个指定大小的ImageSource 对象

/// <summary>

/// 创建WPF使用的ImageSource类型缩略图(不放大小

图)

/// </summary>

/// <param name="fileName">本地图片路径

</param>

/// <param name="width">指定宽度</param>

/// <param name="height">指定高度</param> public static ImageSource CreateImageSourceThumbnia(string fileName, double width, double height)

{

System.Drawing.Image sourceImage =

System.Drawing.Image.FromFile(fileName);

double rw = width / sourceImage.Width;

double rh = height / sourceImage.Height;

var aspect = (float)Math.Min(rw, rh);

int w = sourceImage.Width, h = sourceImage.Height; if (aspect < 1)

{

w = (int)Math.Round(sourceImage.Width * aspect); h = (int)Math.Round(sourceImage.Height * aspect);

}

Bitmap sourceBmp = new Bitmap(sourceImage, w, h);

IntPtr hBitmap = sourceBmp.GetHbitmap(); BitmapSource bitmapSource =

Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty,

BitmapSizeOptions.FromEmptyOptions()); bitmapSource.Freeze();

System.Utility.Win32.Win32.DeleteObject(hBitmap); sourceImage.Dispose();

sourceBmp.Dispose();

return bitmapSource;

}

2.3 从Bitmap创建指定大小的ImageSource对象

/// <summary>

/// 从一个Bitmap创建ImageSource

/// </summary>

/// <param name="image">Bitmap对象</param> /// <returns></returns>

public static ImageSource CreateImageSourceFromImage(Bitmap image)

{

if (image == null) return null;

try

{

IntPtr ptr = image.GetHbitmap();

BitmapSource bs =

Imaging.CreateBitmapSourceFromHBitmap(ptr, IntPtr.Zero, Int32Rect.Empty,

BitmapSizeOptions.FromEmptyOptions());

bs.Freeze();

image.Dispose();

System.Utility.Win32.Win32.DeleteObject(ptr);

return bs;

}

catch (Exception)

{

return null;

}

}

2.4 从数据流byte[]创建指定大小的ImageSource对象/// <summary>

/// 从数据流创建缩略图

/// </summary>

public static ImageSource CreateImageSourceThumbnia(byte[] data, double width, double height)

{

using (Stream stream = new MemoryStream(data, true)) {

using (Image img = Image.FromStream(stream))

{

return CreateImageSourceThumbnia(img, width, height);

}

}

}

三.自定义缩略图控件ThumbnailImage

ThumbnailImage控件的主要解决的问题:

为了能扩展支持多种类型的缩略图,设计了一个简单的模式,用VS自带的工具生成的代码视图:

3.1 多种类型的缩略图扩展

首先定义一个图片类型枚举:

/// <summary>

/// 缩略图数据源源类型

/// </summary>

public enum EnumThumbnail

{

Image,

Vedio,

WebImage,

Auto,

FileX,

}

然后定义了一个接口,生成图片数据源ImageSource

/// <summary>

/// 缩略图创建服务接口

/// </summary>

public interface IThumbnailProvider

{

/// <summary>

/// 创建缩略图。fileName:文件路径;width:图片宽度;height:高度

/// </summary>

ImageSource GenereateThumbnail(object fileSource, double width, double height);

}

如上面的代码视图,有三个实现,视频缩略图VedioThumbnailProvider没有实现完成,基本方法是利用一个第三方工具ffmpeg来获取第一帧图像然后创建ImageSource。

ImageThumbnailProvider:普通图片缩略图实现(调用的2.2方法):

/// <summary>

/// 本地图片缩略图创建服务

/// </summary>

internal class ImageThumbnailProvider : IThumbnailProvider

{

/// <summary>

/// 创建缩略图。fileName:文件路径;width:图片宽度;height:高度

/// </summary>

public ImageSource GenereateThumbnail(object fileName, double width, double height)

{

try

{

var path = fileName.ToSafeString();

if (path.IsInvalid()) return null;

return

System.Utility.Helper.Images.CreateImageSourceThumbni a(path, width, height);

}

catch

{

return null;

}

}

}

WebImageThumbnailProvider:网络图片缩略图实现(下载图片数据后调用2.1方法):

/// <summary>

/// 网络图片缩略图创建服务

/// </summary>

internal class WebImageThumbnailProvider : IThumbnailProvider

{

/// <summary>

/// 创建缩略图。fileName:文件路径;width:图片宽度;height:高度

/// </summary>

public ImageSource GenereateThumbnail(object fileName, double width, double height)

{

try

{

var path = fileName.ToSafeString();

if (path.IsInvalid()) return null;

var request = WebRequest.Create(path);

request.Timeout = 20000;

var stream =

request.GetResponse().GetResponseStream();

var img =

System.Drawing.Image.FromStream(stream);

return

System.Utility.Helper.Images.CreateImageSourceThumbni a(img, width, height);

}

catch

{

return null;

}

}

}

简单工厂ThumbnailProviderFactory实现:

/// <summary>

/// 缩略图创建服务简单工厂

/// </summary>

public class ThumbnailProviderFactory :

System.Utility.Patterns.ISimpleFactory<EnumThumbnail, IThumbnailProvider>

{

/// <summary>

/// 根据key获取实例

/// </summary>

public virtual IThumbnailProvider

GetInstance(EnumThumbnail key)

{

switch (key)

{

case EnumThumbnail.Image:

return

Singleton<ImageThumbnailProvider>.GetInstance();

case EnumThumbnail.Vedio:

return

Singleton<VedioThumbnailProvider>.GetInstance();

case EnumThumbnail.WebImage:

return

Singleton<WebImageThumbnailProvider>.GetInstanc e();

}

return null;

}

}

3.2 缩略图控件ThumbnailImage

先看看效果图吧,下面三张图片,图1是本地图片,图2是网络图片,图3也是网络图片,为什么没显示呢,这张图片用的是国外的图片链接地址,异步加载(加载比较慢,还没出来的!)

ThumbnailImage实际是继承在微软的图片控件Image,因此没有样式代码,继承之后,主要的目的就是重写Imagesource的处理过程,详细代码:

/*

* 较大的图片,视频,网络图片要做缓存处理:缓存缩略图为本地文件,或内存缩略图对象。

*/

/// <summary>

/// 缩略图图片显示控件,同时支持图片和视频缩略图

/// </summary>

public class ThumbnailImage : Image

{

/// <summary>

/// 是否启用缓存,默认false不启用

/// </summary>

public bool CacheEnable

{

get { return

(bool)GetValue(CacheEnableProperty); }

set { SetValue(CacheEnableProperty, value); }

}

/// <summary>

/// 是否启用缓存,默认false不启用.默认缓存时间是180秒

/// </summary>

public static readonly DependencyProperty CacheEnableProperty =

DependencyProperty.Register("CacheEnable", typeof(bool), typeof(ThumbnailImage), new PropertyMetadata(false));

/// <summary>

/// 缓存时间,单位秒。默认180秒

/// </summary>

public int CacheTime

{

get { return (int)GetValue(CacheTimeProperty); }

set { SetValue(CacheTimeProperty, value); }

}

public static readonly DependencyProperty CacheTimeProperty =

DependencyProperty.Register("CacheTime", typeof(int), typeof(ThumbnailImage), new PropertyMetadata(180));

/// <summary>

/// 是否启用异步加载,网络图片建议启用,本地图可以不需要。默认不起用异步

/// </summary>

public bool AsyncEnable

{

get { return

(bool)GetValue(AsyncEnableProperty); }

set { SetValue(AsyncEnableProperty, value); }

}

public static readonly DependencyProperty AsyncEnableProperty =

DependencyProperty.Register("AsyncEnable", typeof(bool), typeof(ThumbnailImage), new PropertyMetadata(false));

/// <summary>

/// 缩略图类型,默认Image图片

/// </summary>

public EnumThumbnail ThumbnailType

{

get { return

(EnumThumbnail)GetValue(ThumbnailTypeProperty); } set { SetValue(ThumbnailTypeProperty, value); }

}

public static readonly DependencyProperty ThumbnailTypeProperty =

DependencyProperty.Register("ThumbnailType", typeof(EnumThumbnail), typeof(ThumbnailImage), new PropertyMetadata(EnumThumbnail.Image));

/// <summary>

/// 缩略图数据源:文件物理路径

/// </summary>

public object ThumbnailSource

{

get { return GetValue(ThumbnailSourceProperty); }

set { SetValue(ThumbnailSourceProperty, value); } }

public static readonly DependencyProperty ThumbnailSourceProperty =

DependencyProperty.Register("ThumbnailSource", typeof(object),

typeof(ThumbnailImage), new PropertyMetadata(OnSourcePropertyChanged));

/// <summary>

/// 缩略图

/// </summary>

protected static ThumbnailProviderFactory ThumbnailProviderFactory = new ThumbnailProviderFactory();

protected override void OnInitialized(EventArgs e)

{

base.OnInitialized(e);

this.Loaded += ThumbnailImage_Loaded;

}

void ThumbnailImage_Loaded(object sender, RoutedEventArgs e)

{

BindSource(this);

}

/// <summary>

/// 属性更改处理事件

/// </summary>

private static void

OnSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)

{

ThumbnailImage img = sender as ThumbnailImage;

if (img == null) return;

if (!img.IsLoaded) return;

BindSource(img);

}

private static void BindSource(ThumbnailImage image)

{

var w = image.Width;

var h = image.Height;

object source = image.ThumbnailSource;

//bind

if (image.AsyncEnable)

{

BindThumbnialAync(image, source, w, h);

}

else

{

BindThumbnial(image, source, w, h);

}

}

/// <summary>

/// 绑定缩略图

wpf自定义slider控件

自定义Slider控件 最终效果: 界面: