如何在WPF中加载大批量数据,并且不会阻塞UI线程,尤其是加载大量图片时,这活儿一直是很多朋友都相当关注的。世上没有最完美的解决之道,咱们但求相对较优的方案。

经过一些试验和对比,老周找到了一种算是不错的方案,重点是这个方案比较简单,无须闯五关斩六将,只要你对数据绑定有些基础就好了。

好,F话少扯,咱们开始吧。

老周手里没有那么多照片,那就用同一张图片做测试吧。假设我要在应用程序运行时加载 2 万张图片,我想2W张应该可以了,没见过谁会傻到要加载100W张那么变态。

大致情况是:数据源集合是一个 ObservableCollection<Uri>, 也就是说集合中放的是图像的URI,为什么不放BitmapSource 呢,因为 DependencyObject 是不能跨线程操作的,只能在UI线程上创建。默认情况下,ObservableCollection<T>也不能在非UI线程上操作,不过,我可以通过调用以下方法来让它可以跨线程操作:

public static void EnableCollectionSynchronization(IEnumerable collection, object lockObject)

这个方法是 BindingOperations 类公开的静态方法,可以在窗口的构造函数中调用它,而且一定要在操作集合之前调用。调用时,把 ObservableCollection 集合传递给 collection 参数,第二个参数lockObject 是一个自定义对象,它指的是可以在线程间同步时引用的对象,在异步代码中,可以把这个对象写在一个 lock 语句块中。主要用途是防止UI访问集合的过程中,集合被其他线程意外修改。

下面代码开启跨线程访问集合支持:

            images = new ObservableCollection<Uri>();
……
lbImages.SetBinding(ItemsControl.ItemsSourceProperty, b); // 这一句很关键,开启集合的异步访问支持
BindingOperations.EnableCollectionSynchronization(images, lockobj);

然后在窗口的构造函数中,执行一个新 Task,用一个新线程来加载数据。

            Task.Run(() =>
{
// 代码写在 lock 块中
lock (lockobj)
{
for (int i = ; i < ; i++)
{
Uri u = new Uri("0.jpg", UriKind.Relative);
images.Add(u);
}
}
});

开始一个新Task是为了让主线程不受阻止,可以继续响应UI操作。

由于集合中都是 URI,而界面上显示的是图像,可以弄一个自定义的数据转换器,转换为位图。

    public sealed class UriToBitmapConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Uri uri = (Uri)value;
BitmapImage bmp = new BitmapImage();
bmp.DecodePixelHeight = ; // 确定解码高度,宽度不同时设置
bmp.BeginInit();
// 延迟,必要时创建
bmp.CreateOptions = BitmapCreateOptions.DelayCreation;
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.UriSource = uri;
bmp.EndInit(); //结束初始化
return bmp;
} public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}

因为是单向转换,所以ConvertBack就免了。

注意,在实例化BitmapImage时,DecodePixelHeight 和 DecodePixelWidth 属性只能设置任意一个,不要同时设置,不然图片的比例会变形。如果我们界面用的图不需要很大,就设一个小的值,比如200像素,这样可以节约性能。

还可以把 CreateOptions 属性设为 DelayCreation ,这样只在图像需要时才会创建,也省了一些性能。

为了让这个转换器能在XAML代码中访问,需要把它的实例声明在UI的资源列表中。

        <Grid.Resources>
<local:UriToBitmapConverter x:Key="tobmpcvt"/>
</Grid.Resources>

接下来就是用Binding了,实现界面绑定。

        <ListBox Name="lbImages" ScrollViewer.IsDeferredScrollingEnabled="False"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Height="200" Width="200" Source="{Binding IsAsync=True,Converter={StaticResource tobmpcvt}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

使用 Binding 时,把 IsAsync 属性设为 True,这样允许界面使用辅助线程来绑定数据,记得,记得。

这样就完成了,然后我们可以运行,让程序加载 2万个图像。这时候会发现,程序运行后不会卡住了,而且把滚动往下拖动时,会自动加载数据。

如何?这效果不错吧。

示例源代码下载地址

【.net深呼吸】WPF异步加载大批量图像的更多相关文章

  1. WPF 异步加载窗体

    加载某个界面时,需要获取数据,而数据返回的时间比较长,这个时候可以异步加载界面. 1.在该窗体的加载事件(Load)中编写以下代码: new Thread(p=>{DataBinding();} ...

  2. WPF 异步加载高清大图

    不管什么东西,但凡太大了,总是让人又爱又恨啊!(很有道理的样子,大家鼓掌└( ̄  ̄└)(┘ ̄  ̄)┘) 猿:老板,现在这社会啊,真是浮躁啊,之前还是什么1080P,然后就到了2K,现在又到了4K……他 ...

  3. wpf 异步加载 只需6段代码

    private BackgroundWorker worker = null; ProgressBar probar = new ProgressBar(); private int percentV ...

  4. WPF 多线程 UI:设计一个异步加载 UI 的容器

    对于 WPF 程序,如果你有某一个 UI 控件非常复杂,很有可能会卡住主 UI,给用户软件很卡的感受.但如果此时能有一个加载动画,那么就不会感受到那么卡顿了.UI 的卡住不同于 IO 操作或者密集的 ...

  5. Android异步加载图像(含线程池,缓存方法)

    研究了android从网络上异步加载图像: (1)由于android UI更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到以下方法. 在主线程中new 一个Han ...

  6. 转: Android异步加载图像小结

    转:http://blog.csdn.net/sgl870927/article/details/6285535 研究了android从网络上异步加载图像,现总结如下: (1)由于android UI ...

  7. WPF技术触屏上的应用系列(五): 图片列表异步加载、手指进行缩小、放大、拖动 、惯性滑入滑出等效果

    原文:WPF技术触屏上的应用系列(五): 图片列表异步加载.手指进行缩小.放大.拖动 .惯性滑入滑出等效果 去年某客户单位要做个大屏触屏应用,要对档案资源进行展示之用.客户端是Window7操作系统, ...

  8. (转)利用WPF的ListView进行大数据量异步加载

    原文:http://www.cnblogs.com/scy251147/archive/2012/01/08/2305319.html 由于之前利用Winform的ListView进行大数据量加载的时 ...

  9. 利用WPF的ListView进行大数据量异步加载

    原文:利用WPF的ListView进行大数据量异步加载 由于之前利用Winform的ListView进行大数据量加载的时候,诟病良多,所以今天试着用WPF的ListView来做了一下,结果没有让我失望 ...

随机推荐

  1. 【腾讯bugly干货分享】HTML 5 视频直播一站式扫盲

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1277 视频直 ...

  2. 探索C#之6.0语法糖剖析

    阅读目录: 自动属性默认初始化 自动只读属性默认初始化 表达式为主体的函数 表达式为主体的属性(赋值) 静态类导入 Null条件运算符 字符串格式化 索引初始化 异常过滤器when catch和fin ...

  3. ABP文档 - Mvc 控制器

    文档目录 本节内容: 简介 AbpController基类 本地化 其它 过滤 异常处理和结果包装 审计日志 验证 授权 工作单元 反伪造 模型绑定器 简介 ABP通过nuget包Abp.Web.Mv ...

  4. [EasyUI美化换肤]更换EasyUi图标

    前言 本篇文章主要是记录一些换EasyUI皮肤的过程,备忘.也欢迎美工大神各路UI给点好意见,EasyUI我就不介绍了,自行百度吧..(So..所以别问我是不是响应式..本身EasyUI就不是响应式. ...

  5. javascript动画系列第二篇——磁性吸附

    × 目录 [1]范围限定 [2]拖拽范围 [3]磁性吸附 前面的话 上一篇,我们介绍了元素拖拽的实现.但在实际应用中,常常需要为拖拽的元素限定范围.而通过限定范围,再增加一些辅助的措施,就可以实现磁性 ...

  6. ABP创建数据库操作步骤

    1 ABP创建数据库操作步骤 1.1 SimpleTaskSystem.Web项目中的Web.config文件修改数据库配置. <add name="Default" pro ...

  7. ES6之let命令详解

    let与块级作用域 { var foo='foo'; let bar='bar'; } console.log(foo,'var'); //foo varconsole.log(bar ,'bar') ...

  8. python select网络编程详细介绍

    刚看了反应堆模式的原理,特意复习了socket编程,本文主要介绍python的基本socket使用和select使用,主要用于了解socket通信过程 一.socket模块 socket - Low- ...

  9. Linux实战教学笔记06:Linux系统基础优化

    第六节 Linux系统基础优化 标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 基础环境 第2章 使用网易163镜像做yum源 默认国外的yum源速度很慢,所以换成国内的. 第一步:先备份 ...

  10. 前端开发小白必学技能—非关系数据库又像关系数据库的MongoDB快速入门命令(2)

    今天给大家道个歉,没有及时更新MongoDB快速入门的下篇,最近有点小忙,在此向博友们致歉.下面我将简单地说一下mongdb的一些基本命令以及我们日常开发过程中的一些问题.mongodb可以为我们提供 ...