C# 使用MVVM在WPF中异步加载图像
我正在制作一个应用程序,从本地目录或文件名列表加载多个图像。我是WPF和MVVM的新手,在并发方面从来没有这么出色过。所以我的问题是,我的代码中缺少了什么使图像异步加载C# 使用MVVM在WPF中异步加载图像,c#,wpf,mvvm,async-await,C#,Wpf,Mvvm,Async Await,我正在制作一个应用程序,从本地目录或文件名列表加载多个图像。我是WPF和MVVM的新手,在并发方面从来没有这么出色过。所以我的问题是,我的代码中缺少了什么使图像异步加载 public class ThumbnailGridViewModel : ViewModelBase { public ObservableCollection<Picture> Pictures { get { if (_filterOn)
public class ThumbnailGridViewModel : ViewModelBase
{
public ObservableCollection<Picture> Pictures
{
get
{
if (_filterOn)
{
return new ObservableCollection<Picture>(
_pictures.Where(pic => pic.ImageName.Contains(_searchFilter)).ToList());
}
else
{
return _pictures;
}
}
set
{
_pictures = value;
NotifyPropertyChanged();
}
}
public ThumbnailGridViewModel(IDialogService modalDialog)
{
//...
Messenger.Default.Register<ObservableCollection<Picture>>(this, OnPicturesLoaded);
//...
}
private void OnPicturesLoaded(ObservableCollection<Picture> pictures)
{
Pictures = pictures;
}
}
public class VepixWindowViewModel : ViewModelBase
{
private async void OnOpenFolder(SearchOption option)
{
FolderBrowserDialog folderDialog = new FolderBrowserDialog();
if (folderDialog.ShowDialog() == DialogResult.OK)
{
List<Picture> pictures = await (_pictureRepo.GetPicturesFromFolderAsync(folderDialog.SelectedPath, option));
Messenger.Default.Send(new ObservableCollection<Picture>(pictures));
}
}
private async void OnOpenFiles()
{
OpenFileDialog fileDialog = new OpenFileDialog()
{
Multiselect = true,
Filter = "Image Files|*.jpg;*.jpeg;*.png;*.gif"
};
if (fileDialog.ShowDialog() == DialogResult.OK)
{
List<Picture> pictures = await (_pictureRepo.GetPicturesAsync(fileDialog.FileNames));
Messenger.Default.Send(new ObservableCollection<Picture>(pictures));
}
}
}
public class PictureRepository : IPictureRepository
{
public PictureRepository()
{
_fileService = new FileService();
}
public async Task<List<Picture>> GetPicturesAsync(string[] files)
{
var fileBytes = await _fileService.ReadBytesFromFilesAsync(files.ToList());
return await LoadPicturesAsync(fileBytes);
}
public async Task<List<Picture>> GetPicturesFromFolderAsync(string folderPath, SearchOption option)
{
var fileBytes = await _fileService.GetFilesAndBytesFromDirectoryAsync(folderPath, _supportedImagesFilterList, option);
return await LoadPicturesAsync(fileBytes);
}
private Task<List<Picture>> LoadPicturesAsync(Dictionary<string, byte[]> fileBytes)
{
return Task.Factory.StartNew<List<Picture>>(() =>
{
List<Picture> pictures = new List<Picture>();
Parallel.ForEach(fileBytes, file =>
{
pictures.Add(new Picture(BitmapService.ConvertByteArrayToBitmapImage(file.Value), file.Key));
});
return pictures;
});
}
}
public class FileService : IFileService
{
public async Task<Dictionary<string, byte[]>> GetFilesAndBytesFromDirectoryAsync(string path, List<string> searchPattern, SearchOption option)
{
var files = new List<string>();
if (Directory.Exists(path))
{
await Task.Factory.StartNew(() =>
searchPattern.ForEach( sp =>
files.AddRange(Directory.GetFiles(path, sp, option))));
}
return await ReadBytesFromFilesAsync(files);
}
public byte[] ReadBytesFromFile(string file)
=> File.ReadAllBytes(file);
public async Task<Dictionary<string, byte[]>> ReadBytesFromFilesAsync(List<string> files)
{
//todo: i think i will change this return type to just List<byte[]>
var bytes = new Dictionary<string, byte[]>();
await Task.Factory.StartNew(() =>
files.ForEach(file => bytes.Add(file, ReadBytesFromFile(file))));
return bytes;
}
}
public static class BitmapService
{
public static BitmapImage ConvertByteArrayToBitmapImage(Byte[] bytes)
{
var image = new BitmapImage();
using (var stream = new MemoryStream(bytes))
{
stream.Seek(0, SeekOrigin.Begin);
image.BeginInit();
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
image.Freeze();
}
return image;
}
}
公共类ThumbnailGridViewModel:ViewModelBase
{
公众可观察收集图片
{
得到
{
如果(_filterOn)
{
返回新的ObservableCollection(
_pictures.Where(pic=>pic.ImageName.Contains(_searchFilter)).ToList();
}
其他的
{
返回图片;
}
}
设置
{
_图片=价值;
NotifyPropertyChanged();
}
}
公共ThumbnailGridViewModel(IDialogService modalDialog)
{
//...
Messenger.Default.Register(此,OnPicturesLoaded);
//...
}
已加载图片上的私有void(可观察收集图片)
{
图片=图片;
}
}
公共类VepixWindowViewModel:ViewModelBase
{
专用异步void OnOpenFolder(SearchOption选项)
{
FolderBrowserDialog folderDialog=新建FolderBrowserDialog();
如果(folderDialog.ShowDialog()==DialogResult.OK)
{
List pictures=wait(_pictureRepo.GetPicturesFromFolderAsync(folderDialog.SelectedPath,option));
senger.Default.Send(新的ObservableCollection(图片));
}
}
私有异步void OnOpenFiles()
{
OpenFileDialog fileDialog=新建OpenFileDialog()
{
Multiselect=true,
Filter=“图像文件|*.jpg;*.jpeg;*.png;*.gif”
};
if(fileDialog.ShowDialog()==DialogResult.OK)
{
List pictures=wait(_pictureRepo.GetPicturesAsync(fileDialog.FileNames));
senger.Default.Send(新的ObservableCollection(图片));
}
}
}
公共类PictureRepository:IPictureRepository
{
公共图片存储
{
_fileService=newfileservice();
}
公共异步任务GetPicturesAsync(字符串[]文件)
{
var fileBytes=wait_fileService.readbytesfromfileasync(files.ToList());
返回等待加载图片同步(fileBytes);
}
公共异步任务GetPicturesFromFolderAsync(字符串folderPath,搜索选项)
{
var fileBytes=await _fileService.getfilesandbytesfromtirectoryasync(folderPath,_supportedImagesFilterList,option);
返回等待加载图片同步(fileBytes);
}
专用任务加载图片同步(字典文件字节)
{
返回Task.Factory.StartNew(()=>
{
列表图片=新列表();
Parallel.ForEach(fileBytes,file=>
{
添加(新图片(BitmapService.ConvertByteArrayToBitmapImage(file.Value),file.Key));
});
返回图片;
});
}
}
公共类文件服务:IFileService
{
公共异步任务GetFilesandBytesFromDirectorySync(字符串路径、列表搜索模式、搜索选项)
{
var files=新列表();
if(目录存在(路径))
{
等待任务。工厂。开始新建(()=>
searchPattern.ForEach(sp=>
files.AddRange(Directory.GetFiles(path、sp、option));
}
返回wait readbytesfromfileasync(文件);
}
公共字节[]ReadBytesFromFile(字符串文件)
=>File.ReadAllBytes(文件);
公共异步任务ReadBytesFromFileAsync(列出文件)
{
//todo:我想我会将此返回类型更改为仅列表
var bytes=新字典();
等待任务。工厂。开始新建(()=>
files.ForEach(file=>bytes.Add(file,ReadBytesFromFile(file)));
返回字节;
}
}
公共静态类位图服务
{
公共静态BitmapImage ConvertByteArrayToBitmapImage(字节[]字节)
{
var image=新的位图图像();
使用(变量流=新内存流(字节))
{
stream.Seek(0,SeekOrigin.Begin);
image.BeginInit();
image.StreamSource=流;
image.CacheOption=BitmapCacheOption.OnLoad;
image.EndInit();
image.Freeze();
}
返回图像;
}
}
在不检查所有代码的情况下,在ConvertByteArrayToBitmapImage
方法中,您应该在调用EndInit
之前设置image.CacheOption=BitmapCacheOption.OnLoad
,并处理内存流(理想情况下使用块将其放入中)。@Clemens,感谢您的输入。我已经做了更改。您也可以调用image.Freeze()
使其跨线程访问。然后您可以在调度程序外部调用convertbytearrayotobitmapimage
。调用@Clemens,再次感谢。我认为我不再需要Dispatcher.Invoke()了,所以我去掉了它。我现在离实现异步越来越近了。似乎只有当集合添加到“图片收藏”属性时,UI才会冻结。我需要进一步调查。