我有以下代码片段,我用它来创建一个List,作为WPF应用程序中的绑定添加到scrollviewer中:
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;
}
}
这个工作正常,直到我添加了BackgroundWorker来处理它。现在,当执行到达
时Image thumbnail = new Image();
我得到以下异常:
System.InvalidOperationException: 'The calling thread must be STA, because many UI components require this.'
两个问题:
(1)如何处理此代码以允许后台工作者处理Image,或者,(2)是否有更好的方法来执行我正在做的事情以允许BackgroundWorker工作?
我没有在多线程环境中工作的经验。我希望它以这种方式工作,因为我处理的最大记录有180个图像,并创建大约10-15秒的挂起。
答案 0 :(得分:2)
不要在后面的代码中创建Image元素。相反,使用带有适当ItemTemplate的ItemControl:
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Images}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
将其绑定到如下所示的视图模型,该模型能够异步加载图像文件。重要的是,BitmapImages被冻结以使它们可以跨线程访问。
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;
}
}
初始化如下:
private ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewModel;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
...
await viewModel.LoadImagesAsync(..., "*.jpg"));
}
另一种视图模型方法可以直接从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;
}
}