如何在 C# 的后台线程中创建 BitmapImage 缓冲区?
How to create a BitmapImage buffer in background thread on C#?
您好,我想创建一个 BitmapImage 缓冲区,该缓冲区是使用来自后台工作人员的队列构建的。主要目标是从私有网络加载一些图像并且不要阻止UI。我可以在不阻塞 UI 的情况下创建该缓冲区,但是当我尝试从队列中获取一张图像时,我得到一个 System.InvalidOperationException
,因此我正在尝试访问另一个线程拥有的对象。
我的员工代码:
private Task<Queue<BitmapImage>> BufferLoader(List<ReadLabel> labels)
{
return Task<Queue<BitmapImage>>.Factory.StartNew(() =>
{
var parameters = CommonData.CurrentRecipe.Parameters["validation"];
var buffer = new Queue<BitmapImage>();
foreach (var label in labels)
{
var fileName = $"{CommonData.CurrentFiche.RegId}-{label.ReadPosition}.bmp";
var validationPath = parameters[ValidationParameters.Parameters.sValidationImagesPath.ToString()].Value.ToString();
var fullPath = Path.Combine(Properties.Settings.Default.CameraImagesBasePath, validationPath, fileName);
try
{
if (File.Exists(fullPath))
{
buffer.Enqueue(new BitmapImage(new Uri(fullPath, UriKind.Absolute)));
}
else
{
throw new ValidationImageNotFoundException(fullPath);
}
}
catch { }
}
return buffer;
});
}
调用方式:
private async void LoadValidationImages()
{
var imageList = new List<ReadLabel>(CommonData.CurrentFiche.Labels);
var images = imageList.FindAll(f => f.CoscNumber.StartsWith("err"));
if (images.Count > 0)
{
Queue<BitmapImage> result = await BufferLoader(images);
ImageBuffer = new Queue<BitmapImage>(result);
BufferLoadingCompleted();
}
}
UI线程调用方法:
private void BufferLoadingCompleted()
{
/*Dispatcher.Invoke(() =>
{*/
imgToValidate.Source = ImageBuffer.Peek();
var parameters = CommonData.CurrentRecipe.Parameters["validation"];
rotation = parameters[ValidationParameters.Parameters.ImageRotation.ToString()].ValueToDouble();
scaleX = parameters[ValidationParameters.Parameters.ImageScaleX.ToString()].ValueToDouble();
scaleY = parameters[ValidationParameters.Parameters.ImageScaleY.ToString()].ValueToDouble();
scrlImage.ScrollToHorizontalOffset(parameters[ValidationParameters.Parameters.ScrollerHorizontalFactor.ToString()].ValueToDouble());
scrlImage.ScrollToVerticalOffset(scrollPosVertical = parameters[ValidationParameters.Parameters.ScrollerVerticalFactor.ToString()].ValueToDouble());
ApplyTransformations();
Console.WriteLine("Load finished");
//});
}
我尝试在 BufferLoadingCompleted()
上使用 Dispatcher.Invoke
但它不起作用,我得到了同样的异常。我做错了什么?
最终代码。 Andy 建议的解决方案:
在我的后台工作程序代码中,我没有 Freeze()
在工作线程中创建新对象,所以我遇到了异常。
解决方案仅适用于后台工作者方法:
private Task<Queue<BitmapImage>> BufferLoader(List<ReadLabel> labels)
{
return Task<Queue<BitmapImage>>.Factory.StartNew(() =>
{
var parameters = CommonData.CurrentRecipe.Parameters["validation"];
var buffer = new Queue<BitmapImage>();
foreach (var label in labels)
{
var fileName = $"{CommonData.CurrentFiche.RegId}-{label.ReadPosition}.bmp";
var validationPath = parameters[ValidationParameters.Parameters.sValidationImagesPath.ToString()].Value.ToString();
var fullPath = Path.Combine(Properties.Settings.Default.CameraImagesBasePath, validationPath, fileName);
try
{
if (File.Exists(fullPath))
{
var newImage = new BitmapImage(new Uri(fullPath, UriKind.Absolute));
newImage.Freeze();
buffer.Enqueue(newImage);
}
else
{
throw new ValidationImageNotFoundException(fullPath);
}
}
catch { }
}
return buffer;
});
}
您正在非 ui 线程上创建默认具有线程关联的内容。
幸运的是,位图图像继承自 freezable。
查看继承链:
https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.imaging.bitmapimage?view=net-5.0
继承:
目的
调度器对象
依赖对象
可冻结
动画化
图片来源
位图源
位图图像
如果您在可冻结对象上调用 .Freeze(),那么您可以在线程之间传递它。
从那里:
什么是可冻结对象?
Freezable 是一种特殊类型的对象,它具有两种状态:未冻结和已冻结。解冻时,Freezable 的行为与任何其他对象一样。冻结后,无法再修改 Freezable。
Freezable 提供了 Changed 事件来通知观察者对象的任何修改。冻结 Freezable 可以提高其性能,因为它不再需要将资源花费在更改通知上。 冻结的 Freezable 也可以跨线程共享,而未冻结的 Freezable 则不能。
您好,我想创建一个 BitmapImage 缓冲区,该缓冲区是使用来自后台工作人员的队列构建的。主要目标是从私有网络加载一些图像并且不要阻止UI。我可以在不阻塞 UI 的情况下创建该缓冲区,但是当我尝试从队列中获取一张图像时,我得到一个 System.InvalidOperationException
,因此我正在尝试访问另一个线程拥有的对象。
我的员工代码:
private Task<Queue<BitmapImage>> BufferLoader(List<ReadLabel> labels)
{
return Task<Queue<BitmapImage>>.Factory.StartNew(() =>
{
var parameters = CommonData.CurrentRecipe.Parameters["validation"];
var buffer = new Queue<BitmapImage>();
foreach (var label in labels)
{
var fileName = $"{CommonData.CurrentFiche.RegId}-{label.ReadPosition}.bmp";
var validationPath = parameters[ValidationParameters.Parameters.sValidationImagesPath.ToString()].Value.ToString();
var fullPath = Path.Combine(Properties.Settings.Default.CameraImagesBasePath, validationPath, fileName);
try
{
if (File.Exists(fullPath))
{
buffer.Enqueue(new BitmapImage(new Uri(fullPath, UriKind.Absolute)));
}
else
{
throw new ValidationImageNotFoundException(fullPath);
}
}
catch { }
}
return buffer;
});
}
调用方式:
private async void LoadValidationImages()
{
var imageList = new List<ReadLabel>(CommonData.CurrentFiche.Labels);
var images = imageList.FindAll(f => f.CoscNumber.StartsWith("err"));
if (images.Count > 0)
{
Queue<BitmapImage> result = await BufferLoader(images);
ImageBuffer = new Queue<BitmapImage>(result);
BufferLoadingCompleted();
}
}
UI线程调用方法:
private void BufferLoadingCompleted()
{
/*Dispatcher.Invoke(() =>
{*/
imgToValidate.Source = ImageBuffer.Peek();
var parameters = CommonData.CurrentRecipe.Parameters["validation"];
rotation = parameters[ValidationParameters.Parameters.ImageRotation.ToString()].ValueToDouble();
scaleX = parameters[ValidationParameters.Parameters.ImageScaleX.ToString()].ValueToDouble();
scaleY = parameters[ValidationParameters.Parameters.ImageScaleY.ToString()].ValueToDouble();
scrlImage.ScrollToHorizontalOffset(parameters[ValidationParameters.Parameters.ScrollerHorizontalFactor.ToString()].ValueToDouble());
scrlImage.ScrollToVerticalOffset(scrollPosVertical = parameters[ValidationParameters.Parameters.ScrollerVerticalFactor.ToString()].ValueToDouble());
ApplyTransformations();
Console.WriteLine("Load finished");
//});
}
我尝试在 BufferLoadingCompleted()
上使用 Dispatcher.Invoke
但它不起作用,我得到了同样的异常。我做错了什么?
最终代码。 Andy 建议的解决方案:
在我的后台工作程序代码中,我没有 Freeze()
在工作线程中创建新对象,所以我遇到了异常。
解决方案仅适用于后台工作者方法:
private Task<Queue<BitmapImage>> BufferLoader(List<ReadLabel> labels)
{
return Task<Queue<BitmapImage>>.Factory.StartNew(() =>
{
var parameters = CommonData.CurrentRecipe.Parameters["validation"];
var buffer = new Queue<BitmapImage>();
foreach (var label in labels)
{
var fileName = $"{CommonData.CurrentFiche.RegId}-{label.ReadPosition}.bmp";
var validationPath = parameters[ValidationParameters.Parameters.sValidationImagesPath.ToString()].Value.ToString();
var fullPath = Path.Combine(Properties.Settings.Default.CameraImagesBasePath, validationPath, fileName);
try
{
if (File.Exists(fullPath))
{
var newImage = new BitmapImage(new Uri(fullPath, UriKind.Absolute));
newImage.Freeze();
buffer.Enqueue(newImage);
}
else
{
throw new ValidationImageNotFoundException(fullPath);
}
}
catch { }
}
return buffer;
});
}
您正在非 ui 线程上创建默认具有线程关联的内容。
幸运的是,位图图像继承自 freezable。 查看继承链:
https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.imaging.bitmapimage?view=net-5.0
继承: 目的 调度器对象 依赖对象 可冻结 动画化 图片来源 位图源 位图图像
如果您在可冻结对象上调用 .Freeze(),那么您可以在线程之间传递它。
从那里:
什么是可冻结对象?
Freezable 是一种特殊类型的对象,它具有两种状态:未冻结和已冻结。解冻时,Freezable 的行为与任何其他对象一样。冻结后,无法再修改 Freezable。
Freezable 提供了 Changed 事件来通知观察者对象的任何修改。冻结 Freezable 可以提高其性能,因为它不再需要将资源花费在更改通知上。 冻结的 Freezable 也可以跨线程共享,而未冻结的 Freezable 则不能。