一、问题
如何在WPF中加载大批量数据,并且不会阻塞UI线程,尤其是加载大量图片时?
二、方案
大致情况是:数据源集合是一个 ObservableCollection<PictureInfo>, 也就是说集合中放的是图像的一个自定义类,包含了Info,PicturePath等属性。界面通过Bind,使用转换器,将路径转换为ImageSource对象即可。不要直接加载ImageSource
PictureInfo:图像信息类
/// <summary>
/// 图片信息类
/// </summary>
public class PictureInfo
{
/// <summary>
/// 图片信息
/// </summary>
public string Info { get; set; }
/// <summary>
/// 图片路径
/// </summary>
public string PicturePath { get; set; }
}
为什么不放BitmapSource属性 呢,因为 DependencyObject 是不能跨线程操作的,只能在UI线程上创建。默认情况下,ObservableCollection<T>也不能在非UI线程上操作,不过,我可以通过调用以下方法来让它可以跨线程操作:
public static void EnableCollectionSynchronization(IEnumerable collection, object lockObject)
这个方法是 BindingOperations 类公开的静态方法,可以在窗口的构造函数中调用它,而且一定要在操作集合之前调用。调用时,把 ObservableCollection 集合传递给 collection 参数,第二个参数lockObject 是一个自定义对象,它指的是可以在线程间同步时引用的对象,
三、实例
XAML
<ListBox x:Name="imagListBox"
VirtualizingStackPanel.IsVirtualizing="True"
ItemContainerStyle="{StaticResource ImageItemStyle}"
ItemsSource="{Binding ImageList, Mode=OneWay,IsAsync=True}"
BorderBrush="#eaeaeb"
Margin="5"/>
<Style x:Key="ImageItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Part_Border"
Width="{TemplateBinding ListBoxItem.Width}"
Height="{TemplateBinding ListBoxItem.Height}"
Background="{TemplateBinding ListBoxItem.Background}">
<Grid x:Name="Part_Grid" Margin="2">
<Image Grid.Row="1"
Width="240"
Height="180"
Stretch="Uniform"
Source="{Binding PicturePath,IsAsync=True,Converter={StaticResource PathToImage},Mode=OneWay}"></Image>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
转换器
public sealed class PathToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
string uriStr = (string)value;
Uri uri = new Uri(uriStr, UriKind.Absolute);
BitmapImage bmp = new BitmapImage();
bmp.DecodePixelHeight = 250; // 确定解码高度,宽度不同时设置
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;
}
}
c# 后台
private ObservableCollection<PictureInfo> imageList = new ObservableCollection<PictureInfo>();
public ObservableCollection<PictureInfo> ImageList
{
get { return imageList; }
set
{
NotifyChanged("ImageList");
imageList = value;
}
}
object lockobj = new object();
/// <summary>
/// 加载缺陷图片
/// </summary>
/// <param name="strPath"></param>
public void LoadPicture(string strPath)
{
ImageList.Clear();
BindingOperations.EnableCollectionSynchronization(ImageList, lockobj);
string folderFullName = strPath ;//图片文件夹路径
Task.Run(() =>
{
DirectoryInfo TheFolder = new DirectoryInfo(folderFullName);
var res = TheFolder.GetFiles();
for (int i = 0; i < res.Length; i++)
{
PictureInfo pictureInfo = new PictureInfo();
pictureInfo.PicturePath = res[i].DirectoryName + @"\" + res[i].Name;
Uri uri = new Uri(pictureInfo.PicturePath, UriKind.Absolute);
BitmapImage myimg = ImageProcess.GetBitImage(uri);
pictureInfo.Info = res[i].Name;
ImageList.Add(pictureInfo);
}
});
}
参考: