WPF应用程序中后台工作者中的图像

时间:2018-02-20 18:26:49

标签: c# wpf image

我有以下代码片段,我用它来创建一个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秒的挂起。

1 个答案:

答案 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;
    }
}