BitmapFrame в другом потоке
Я использую WPF BackgroundWorker для создания миниатюр. Моя рабочая функция выглядит так:
private void work(object sender, DoWorkEventArgs e)
{
try
{
var paths = e.Argument as string[];
var boxList = new List<BoxItem>();
foreach (string path in paths)
{
if (!string.IsNullOrEmpty(path))
{
FileInfo info = new FileInfo(path);
if (info.Exists && info.Length > 0)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.DecodePixelWidth = 200;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(info.FullName);
bi.EndInit();
var item = new BoxItem();
item.FilePath = path;
MemoryStream ms = new MemoryStream();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bi));
encoder.Save(ms);
item.ThumbNail = ms.ToArray();
ms.Close();
boxList.Add(item);
}
}
}
e.Result = boxList;
}
catch (Exception ex)
{
//nerver comes here
}
}
Когда эта функция завершена и перед запуском функции "Завершено" BackgroundWorker, я вижу в окне вывода Vs2008, что генерируется исключение. Это выглядит как:
A first chance exception of type 'System.NotSupportedException' occurred in PresentationCore.dll
Количество генерируемых исключений равно числу генерируемых миниатюр.
Используя метод "проб и ошибок", я изолировал проблему: BitmapFrame.Create(bi)
Удаление этой строки (делает мою функцию бесполезной) также удаляет исключение.
Я не нашел никакого объяснения этому или лучшего способа создания миниатюр в фоновом потоке.
2 ответа
Лассе, я считаю, что проблема возникает из-за того, что вы выполняете действия вне потока пользовательского интерфейса, которые необходимо выполнить в потоке пользовательского интерфейса. Я полагаю, что создание элементов пользовательского интерфейса (BitmapImage, BitmapFrame) и добавление в контейнеры пользовательского интерфейса должно выполняться в потоке пользовательского интерфейса. (Кто-то поправит меня, если я здесь не прав).
Есть несколько способов создать эти элементы в потоке пользовательского интерфейса, не блокируя приложение в течение чрезмерного периода времени. Наиболее простым, вероятно, является использование события BackgroundWorker's ProgressChanged. ProgressChanged вызывается в потоке пользовательского интерфейса, что делает его идеальным для этой ситуации.
Вы можете использовать событие ProgressChanged рабочего и передать ему путь, необходимый для загрузки миниатюры в аргументе UserState.
Спасибо за ваш вклад. Это заставило начать искать другие решения, и я придумала это.
try
{
var paths = e.Argument as string[];
var boxList = new List<BoxItem>();
foreach (string path in paths)
{
using (Image photoImg = Image.FromFile(path))
{
int newWidth = 200;
int width = newWidth;
int height = (photoImg.Height * newWidth) / photoImg.Width;
var thumbnail = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)thumbnail))
{
g.DrawImage(photoImg, 0, 0, width, height);
using (var ms = new System.IO.MemoryStream())
{
var item = new BoxItem();
item.FilePath = path;
thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
item.ThumbNail = ms.ToArray();
boxList.Add(item);
}
}
}
}
e.Result = boxList;
}
catch (Exception exp)
{
}
Не использует элементы интерфейса... и работает хорошо. Благодарю. // Ласс