Асинхронная загрузка изображений wpf
Есть ListView, каждый из элементов которого которого содержит изображение, подгружаемое с сайта
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal" VerticalAlignment="Center">
<Image Source="{Binding Path=AsyncProperty, IsAsync=True}" Width="30" Height="30"></Image>
<Label VerticalAlignment="Center" Style="{StaticResource BodyRegularTextStyle}" Content="{Binding Name}"></Label>
<Label VerticalAlignment="Center" Style="{StaticResource DescriptionTextStyle}" Content="{Binding Path}"></Label>
</StackPanel>
private ImageSource asyncProperty;
public ImageSource AsyncProperty
{
get
{
if (asyncProperty == null)
LoadImage("https://www.google.com/favicon.ico");
return asyncProperty;
}
set
{
LoadImage("https://www.google.com/favicon.ico");
}
}
private async void LoadImage(string url)
{
HttpClient client = new HttpClient();
try
{
BitmapImage img = new BitmapImage();
img.CacheOption = BitmapCacheOption.OnLoad;
img.BeginInit();
img.StreamSource = await client.GetStreamAsync(url);
img.EndInit();
AsyncProperty = img;
}
сatch (HttpRequestException)
{
}
}
Однако, при исполнении выдает ошибку
System.InvalidOperationException: "Вызывающий поток не может получить доступ к данному объекту, так как владельцем этого объекта является другой поток."
Как можно переписать всё это дело? Без асинхронности всё работает, но в случае если происходит ошибка при попытке получать картинку, зависает всё приложение.
Ответы (1 шт):
Автор решения: VladD
→ Ссылка
Я думаю, можно справиться с проблемой проще. Вам не нужно вручную загружать картинку, фреймворк сделает это в фоне за вас. Вот пример:
XAML:
<Image Source="{Binding Uri}"/>
VM:
class VM : INotifyPropertyChanged
{
string uri;
public string Uri
{
get => uri;
set
{
uri = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Uri)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Вот полный пример:
XAML:
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test" Height="200" Width="200">
<StackPanel>
<Button Click="Go">Go</Button>
<Image Height="100" Width="100" Source="{Binding Uri}"/>
</StackPanel>
</Window>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new VM();
}
void Go(object sender, RoutedEventArgs e)
{
var vm = (VM)DataContext;
vm.Uri = "https://deelay.me/1000/https://www.google.com/favicon.ico";
}
}
Результат:
Мне казалось, что нужно использовать атрибут IsAsync=true у Binding, но, как как выяснилось в результате обсуждения с @aepot в комментариях, асинхронная загрузка работает и без этого.
