Lightness
Lightness

Reputation: 261

Why leaps exception: "The calling thread must be STA, because many UI components require this"?

Why leaps exception in the load method? If it is to make simultaneous , everything works well . Tell me, please , what is the problem and how to fix it ?

public class MainViewModel : ViewModelBase
        {
            private readonly List<string> _listImageUri;
            private int _currentIndex;
            private ImageSource _currentImage;
            public ObservableCollection<Image> CustomControls { get; set; } = new ObservableCollection<Image>();
            public MainViewModel()
            {
                _listImageUri = new List<string>();
                Load("путь к папке");
            }

            private async void Load(string s)
            {
                await Task.Run(() =>
                {
                    foreach (var fileInfo in new DirectoryInfo(s).GetFiles())
                    {
                        _listImageUri.Add(fileInfo.FullName);
                        Image img = new Image(); // exception
                        img.Height = 100;
                        img.Margin = new Thickness(5);
                        img.Width = 100;
                        BitmapImage bmp = new BitmapImage();
                        bmp.BeginInit();
                        bmp.UriSource = new Uri(fileInfo.FullName);
                        bmp.DecodePixelWidth = 100;
                        bmp.EndInit();
                        img.Source = bmp;
                        CustomControls.Add(img);
                        OnPropertyChanged(nameof(CustomControls));
                    }
                });
            }
}

Exception

An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll

Additional information: The calling thread must be STA, because many UI components require this.

Upvotes: 0

Views: 955

Answers (2)

Clemens
Clemens

Reputation: 128096

Do not create Image controls in your view model, especially not when you load images asynchronously.

Change your view model to have an ObservableCollection of ImageSource:

public ObservableCollection<ImageSource> Images { get; set; }
    = new ObservableCollection<ImageSource>();

Then in your load method make sure that adding images to the collection is done in the UI thread. To make the BitmapImages cross-thread accessible, you also have to freeze them:

var bitmap = new BitmapImage(new Uri(fileInfo.FullName));
bitmap.Freeze();

Application.Current.Dispatcher.BeginInvoke(new Action(() => Images.Add(bitmap)));

Finally display the images by means of an ItemsControl with an appropriate ItemTemplate:

<ItemsControl ItemsSource="{Binding Images}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

That said, you may also replace the ObservableCollection<ImageSource> by an ObservableCollection<string>, which would hold the image file paths. This is possible because WPF provides built-in conversion from string (and Uri) to ImageSource. Consequently, you would then also not need any asynchronous task anymore.

Upvotes: 3

unkreativ
unkreativ

Reputation: 480

You run your code in a thread. But updating UI components need to run on the WPF UI thread.

So invoke the critical line(s) using Dispatcher.Invoke so that your code is using the UI-Thread

Upvotes: -2

Related Questions