[英]Images in Background Workers in WPF Application
I have the following code snippet that I use to create a List to add to a scrollviewer as a binding in a WPF application: 我有以下代码片段,可用于创建要添加到滚动查看器中的列表,作为WPF应用程序中的绑定:
private void LoadThumbs(object sender, DoWorkEventArgs e)
{
//ClearScreen();
int max = (int)e.Argument;
int current = 0;
foreach (string filename in filenames)
{
Image thumbnail = new Image();
Uri image_path = new Uri(filename);
BitmapImage image = new BitmapImage(image_path);
Thickness thumb_margin = thumbnail.Margin;
thumb_margin.Bottom = 2.5;
thumb_margin.Top = 2.5;
thumb_margin.Left = 2.5;
thumb_margin.Right = 2.5;
thumbnail.Margin = thumb_margin;
thumbnail.Width = 100;
image.DecodePixelWidth = 200;
thumbnail.Source = image;
thumbnail.Tag = filename;
thumbnail.MouseDown += image_Click;
thumbnail.MouseEnter += hand_Over;
thumbnail.MouseLeave += normal_Out;
images.Add(thumbnail);
thumbnail = null;
}
}
This worked fine until I added a BackgroundWorker to process this. 在我添加BackgroundWorker进行处理之前,此方法一直很好。 Now, when execution gets to
现在,当执行到
Image thumbnail = new Image();
I get the following exception: 我得到以下异常:
System.InvalidOperationException: 'The calling thread must be STA, because many UI components require this.'
Two questions: 两个问题:
(1) How can I process this code to allow the background worker to work on Image, or, (2) is there a better way to do what I am doing to allow for the BackgroundWorker to work? (1)如何处理此代码以允许后台工作人员在Image上工作,或者,(2)是否有更好的方法来做我正在做的工作以允许BackgroundWorker工作?
I have zero experience working in a multi-threaded environment. 我在多线程环境中的工作经验为零。 I want it to work this way because the largest record I process has 180 images and creates about a 10-15 second hang.
我希望它以这种方式工作,因为我处理的最大记录具有180张图像,并会产生大约10-15秒的挂起。
Do not create Image elements in code behind. 不要在后面的代码中创建Image元素。 Instead, use an ItemControl with an appropriate ItemTemplate:
而是使用带有适当ItemTemplate的ItemControl:
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Images}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
Bind it to a view model like shown below, which is capable of asynchronously loading the image files. 将其绑定到如下所示的视图模型,该模型能够异步加载图像文件。 It is important that the BitmapImages are frozen to make them cross-thread accessible.
冻结BitmapImage使其跨线程访问很重要。
public class ViewModel
{
public ObservableCollection<ImageSource> Images { get; }
= new ObservableCollection<ImageSource>();
public async Task LoadImagesAsync(IEnumerable<string> filenames)
{
foreach (var filename in filenames)
{
Images.Add(await Task.Run(() => LoadImage(filename)));
}
}
public ImageSource LoadImage(string filename)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.DecodePixelWidth = 200;
bitmap.UriSource = new Uri(filename);
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
}
which is initialized like this: 初始化如下:
private ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewModel;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
...
await viewModel.LoadImagesAsync(..., "*.jpg"));
}
An alternative view model method could load the BitmapImages directly from a FileStream instead of an Uri: 另一种视图模型方法可以直接从FileStream而不是Uri加载BitmapImages:
public ImageSource LoadImage(string filename)
{
using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.DecodePixelWidth = 200;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.