异步加载时图像未出现在 WPF 窗体上
Images not appearing on WPF form when loading asynchronously
我正在尝试显示(在带有自定义 DataTemplate 的列表框中)从多页 tiff 图像中提取的一系列 BitmapSource 帧(缩略图)。当我在 UI 线程上处理 tiff 并将图像直接添加到列表框的项目集合或将它们添加到绑定的 ObservableCollection 时,它们在列表中显示良好。但是,当尝试异步加载每个缩略图时(使用 BackgroundWorker 或使用异步任务),我看到无法解决的行为:
- 第一个缩略图按预期加载
- 下一个和所有后续的缩略图都获得了列表中的项目(我看到了边框),但显示的只是一张空白图像。它显示了正确的项目数量,但在第一个之后没有图像。
我试过尝试冻结缩略图(不好),试图将它们发送回 UI 线程并通过工作人员的 ReportProgress 将它们添加到那里的集合(不好),但我似乎无法让他们展示。
处理 UI 线程(其中 SyncImages 是绑定到我的 ListBox 的 ObservableCollection,而 OnPropertyChanged 处理通知事件):
private void LoadSynchronous()
{
Stream imageStreamSource = new FileStream(ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var decoder = BitmapDecoder.Create(imageStreamSource, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
foreach (var frame in decoder.Frames)
{
//frame.Freeze(); //Tried this but no effect.
SyncImages.Add(frame);
}
OnPropertyChanged("SyncImages");
}
无效(此示例将帧直接添加到列表中,但我也通过绑定尝试过,结果没有差异):
private void LoadAsync(object sender, DoWorkEventArgs e)
{
Stream imageStreamSource = new FileStream(ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var decoder = BitmapDecoder.Create(imageStreamSource, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
foreach (var frame in decoder.Frames)
{
// frame.Freeze();
(sender as BackgroundWorker).ReportProgress(0, frame);
}
OnPropertyChanged("AsyncImages");
}
private void ReportAsyncProgress(object send, ProgressChangedEventArgs e)
{
var frame = (BitmapSource) e.UserState;
LbAsynchronous.Items.Add(frame);
}
希望有人能阐明这里发生的事情。我知道代码可以提取帧,所以即使在异步示例中也必须加载它们,但似乎 UI 线程几乎无法访问保存图像数据的源属性以显示它们在表格上(这就是我尝试冻结的原因)。
如有任何想法,我们将不胜感激!
@Clemens 对原始问题的评论提供了解决方案。确保以负责任的方式关闭文件流并将 BitmapCacheOption
更改为 OnLoad
现在显示异步加载中的每个图像。
异步加载的最终代码如下所示:
private void LoadAsync(object sender, DoWorkEventArgs e)
{
BitmapDecoder decoder;
using (Stream imageStreamSource = new FileStream(ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
decoder = BitmapDecoder.Create(imageStreamSource, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.OnLoad);
}
foreach (var frame in decoder.Frames)
{
frame.Freeze();
(sender as BackgroundWorker).ReportProgress(0, frame);
}
}
private void UpdateAsync(object send, ProgressChangedEventArgs e)
{
SyncImages.Add((BitmapSource)e.UserState);
OnPropertyChanged("SyncImages");
}
我正在尝试显示(在带有自定义 DataTemplate 的列表框中)从多页 tiff 图像中提取的一系列 BitmapSource 帧(缩略图)。当我在 UI 线程上处理 tiff 并将图像直接添加到列表框的项目集合或将它们添加到绑定的 ObservableCollection 时,它们在列表中显示良好。但是,当尝试异步加载每个缩略图时(使用 BackgroundWorker 或使用异步任务),我看到无法解决的行为:
- 第一个缩略图按预期加载
- 下一个和所有后续的缩略图都获得了列表中的项目(我看到了边框),但显示的只是一张空白图像。它显示了正确的项目数量,但在第一个之后没有图像。
我试过尝试冻结缩略图(不好),试图将它们发送回 UI 线程并通过工作人员的 ReportProgress 将它们添加到那里的集合(不好),但我似乎无法让他们展示。
处理 UI 线程(其中 SyncImages 是绑定到我的 ListBox 的 ObservableCollection,而 OnPropertyChanged 处理通知事件):
private void LoadSynchronous()
{
Stream imageStreamSource = new FileStream(ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var decoder = BitmapDecoder.Create(imageStreamSource, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
foreach (var frame in decoder.Frames)
{
//frame.Freeze(); //Tried this but no effect.
SyncImages.Add(frame);
}
OnPropertyChanged("SyncImages");
}
无效(此示例将帧直接添加到列表中,但我也通过绑定尝试过,结果没有差异):
private void LoadAsync(object sender, DoWorkEventArgs e)
{
Stream imageStreamSource = new FileStream(ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var decoder = BitmapDecoder.Create(imageStreamSource, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
foreach (var frame in decoder.Frames)
{
// frame.Freeze();
(sender as BackgroundWorker).ReportProgress(0, frame);
}
OnPropertyChanged("AsyncImages");
}
private void ReportAsyncProgress(object send, ProgressChangedEventArgs e)
{
var frame = (BitmapSource) e.UserState;
LbAsynchronous.Items.Add(frame);
}
希望有人能阐明这里发生的事情。我知道代码可以提取帧,所以即使在异步示例中也必须加载它们,但似乎 UI 线程几乎无法访问保存图像数据的源属性以显示它们在表格上(这就是我尝试冻结的原因)。
如有任何想法,我们将不胜感激!
@Clemens 对原始问题的评论提供了解决方案。确保以负责任的方式关闭文件流并将 BitmapCacheOption
更改为 OnLoad
现在显示异步加载中的每个图像。
异步加载的最终代码如下所示:
private void LoadAsync(object sender, DoWorkEventArgs e)
{
BitmapDecoder decoder;
using (Stream imageStreamSource = new FileStream(ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
decoder = BitmapDecoder.Create(imageStreamSource, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.OnLoad);
}
foreach (var frame in decoder.Frames)
{
frame.Freeze();
(sender as BackgroundWorker).ReportProgress(0, frame);
}
}
private void UpdateAsync(object send, ProgressChangedEventArgs e)
{
SyncImages.Add((BitmapSource)e.UserState);
OnPropertyChanged("SyncImages");
}